Merge pull request #1428 from ethereum/phase0-simplify
Remove Shards/Crosslinks from Phase 0
This commit is contained in:
commit
364781b934
|
@ -5,12 +5,12 @@
|
|||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# 2**10 (= 1,024)
|
||||
SHARD_COUNT: 1024
|
||||
# 2**6 (= 64)
|
||||
MAX_COMMITTEES_PER_SLOT: 64
|
||||
# 2**7 (= 128)
|
||||
TARGET_COMMITTEE_SIZE: 128
|
||||
# 2**12 (= 4,096)
|
||||
MAX_VALIDATORS_PER_COMMITTEE: 4096
|
||||
# 2**11 (= 2,048)
|
||||
MAX_VALIDATORS_PER_COMMITTEE: 2048
|
||||
# 2**2 (= 4)
|
||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
# 2**16 (= 65,536)
|
||||
|
@ -51,12 +51,12 @@ BLS_WITHDRAWAL_PREFIX: 0x00
|
|||
|
||||
# Time parameters
|
||||
# ---------------------------------------------------------------
|
||||
# 6 seconds 6 seconds
|
||||
SECONDS_PER_SLOT: 6
|
||||
# 12 seconds
|
||||
SECONDS_PER_SLOT: 12
|
||||
# 2**0 (= 1) slots 6 seconds
|
||||
MIN_ATTESTATION_INCLUSION_DELAY: 1
|
||||
# 2**6 (= 64) slots 6.4 minutes
|
||||
SLOTS_PER_EPOCH: 64
|
||||
# 2**5 (= 32) slots 6.4 minutes
|
||||
SLOTS_PER_EPOCH: 32
|
||||
# 2**0 (= 1) epochs 6.4 minutes
|
||||
MIN_SEED_LOOKAHEAD: 1
|
||||
# 2**2 (= 4) epochs 25.6 minutes
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
# [customized] Just 8 shards for testing purposes
|
||||
SHARD_COUNT: 8
|
||||
# [customized] Just 4 committees for slot for testing purposes
|
||||
MAX_COMMITTEES_PER_SLOT: 4
|
||||
# [customized] unsecure, but fast
|
||||
TARGET_COMMITTEE_SIZE: 4
|
||||
# 2**12 (= 4,096)
|
||||
MAX_VALIDATORS_PER_COMMITTEE: 4096
|
||||
# 2**11 (= 2,048)
|
||||
MAX_VALIDATORS_PER_COMMITTEE: 2048
|
||||
# 2**2 (= 4)
|
||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
# 2**16 (= 65,536)
|
||||
|
@ -50,7 +50,7 @@ BLS_WITHDRAWAL_PREFIX: 0x00
|
|||
|
||||
# Time parameters
|
||||
# ---------------------------------------------------------------
|
||||
# 6 seconds 6 seconds
|
||||
# [customized] Faster for testing purposes
|
||||
SECONDS_PER_SLOT: 6
|
||||
# 2**0 (= 1) slots 6 seconds
|
||||
MIN_ATTESTATION_INCLUSION_DELAY: 1
|
||||
|
|
|
@ -118,7 +118,7 @@ def apply_constants_preset(preset: Dict[str, Any]) -> None:
|
|||
global_vars[k] = v
|
||||
|
||||
# Deal with derived constants
|
||||
global_vars['GENESIS_EPOCH'] = compute_epoch_of_slot(GENESIS_SLOT)
|
||||
global_vars['GENESIS_EPOCH'] = compute_epoch_at_slot(GENESIS_SLOT)
|
||||
|
||||
# Initialize SSZ types again, to account for changed lengths
|
||||
init_SSZ_types()
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
- [`Fork`](#fork)
|
||||
- [`Checkpoint`](#checkpoint)
|
||||
- [`Validator`](#validator)
|
||||
- [`Crosslink`](#crosslink)
|
||||
- [`AttestationData`](#attestationdata)
|
||||
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
|
||||
- [`IndexedAttestation`](#indexedattestation)
|
||||
|
@ -69,8 +68,8 @@
|
|||
- [`compute_shuffled_index`](#compute_shuffled_index)
|
||||
- [`compute_proposer_index`](#compute_proposer_index)
|
||||
- [`compute_committee`](#compute_committee)
|
||||
- [`compute_epoch_of_slot`](#compute_epoch_of_slot)
|
||||
- [`compute_start_slot_of_epoch`](#compute_start_slot_of_epoch)
|
||||
- [`compute_epoch_at_slot`](#compute_epoch_at_slot)
|
||||
- [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch)
|
||||
- [`compute_activation_exit_epoch`](#compute_activation_exit_epoch)
|
||||
- [`compute_domain`](#compute_domain)
|
||||
- [Beacon state accessors](#beacon-state-accessors)
|
||||
|
@ -82,12 +81,9 @@
|
|||
- [`get_active_validator_indices`](#get_active_validator_indices)
|
||||
- [`get_validator_churn_limit`](#get_validator_churn_limit)
|
||||
- [`get_seed`](#get_seed)
|
||||
- [`get_committee_count`](#get_committee_count)
|
||||
- [`get_crosslink_committee`](#get_crosslink_committee)
|
||||
- [`get_start_shard`](#get_start_shard)
|
||||
- [`get_shard_delta`](#get_shard_delta)
|
||||
- [`get_committee_count_at_slot`](#get_committee_count_at_slot)
|
||||
- [`get_beacon_committee`](#get_beacon_committee)
|
||||
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
|
||||
- [`get_attestation_data_slot`](#get_attestation_data_slot)
|
||||
- [`get_total_balance`](#get_total_balance)
|
||||
- [`get_total_active_balance`](#get_total_active_balance)
|
||||
- [`get_domain`](#get_domain)
|
||||
|
@ -105,7 +101,6 @@
|
|||
- [Epoch processing](#epoch-processing)
|
||||
- [Helper functions](#helper-functions-1)
|
||||
- [Justification and finalization](#justification-and-finalization)
|
||||
- [Crosslinks](#crosslinks)
|
||||
- [Rewards and penalties](#rewards-and-penalties-1)
|
||||
- [Registry updates](#registry-updates)
|
||||
- [Slashings](#slashings)
|
||||
|
@ -129,7 +124,7 @@
|
|||
This document represents the specification for Phase 0 of Ethereum 2.0 -- The Beacon Chain.
|
||||
|
||||
At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of validators. In the initial deployment phases of Ethereum 2.0, the only mechanism to become a validator is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a validator happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and a queuing process is completed. Exit is either voluntary or done forcibly as a penalty for misbehavior.
|
||||
The primary source of load on the beacon chain is "attestations". Attestations are simultaneously availability votes for a shard block and proof-of-stake votes for a beacon block. A sufficient number of attestations for the same shard block create a "crosslink", confirming the shard segment up to that shard block into the beacon chain. Crosslinks also serve as infrastructure for asynchronous cross-shard communication.
|
||||
The primary source of load on the beacon chain is "attestations". Attestations are simultaneously availability votes for a shard block (Phase 1) and proof-of-stake votes for a beacon block (Phase 0).
|
||||
|
||||
## Notation
|
||||
|
||||
|
@ -143,7 +138,7 @@ We define the following Python custom types for type hinting and readability:
|
|||
| - | - | - |
|
||||
| `Slot` | `uint64` | a slot number |
|
||||
| `Epoch` | `uint64` | an epoch number |
|
||||
| `Shard` | `uint64` | a shard number |
|
||||
| `CommitteeIndex` | `uint64` | a committee index at a slot |
|
||||
| `ValidatorIndex` | `uint64` | a validator registry index |
|
||||
| `Gwei` | `uint64` | an amount in Gwei |
|
||||
| `Hash` | `Bytes32` | a hash |
|
||||
|
@ -160,7 +155,7 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
| Name | Value |
|
||||
| - | - |
|
||||
| `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` |
|
||||
| `BASE_REWARDS_PER_EPOCH` | `5` |
|
||||
| `BASE_REWARDS_PER_EPOCH` | `4` |
|
||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||
| `SECONDS_PER_DAY` | `86400` |
|
||||
| `JUSTIFICATION_BITS_LENGTH` | `4` |
|
||||
|
@ -174,16 +169,16 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `SHARD_COUNT` | `2**10` (= 1,024) |
|
||||
| `MAX_COMMITTEES_PER_SLOT` | `2**6` (= 64) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**12` (= 4,096) |
|
||||
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**11` (= 2,048) |
|
||||
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
|
||||
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
|
||||
| `SHUFFLE_ROUND_COUNT` | `90` |
|
||||
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**16` (= 65,536) |
|
||||
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
|
||||
|
||||
- For the safety of crosslinks, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
|
||||
### Gwei values
|
||||
|
||||
|
@ -206,16 +201,15 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
|
||||
| Name | Value | Unit | Duration |
|
||||
| - | - | :-: | :-: |
|
||||
| `SECONDS_PER_SLOT` | `6` | seconds | 6 seconds |
|
||||
| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 6 seconds |
|
||||
| `SLOTS_PER_EPOCH` | `2**6` (= 64) | slots | 6.4 minutes |
|
||||
| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds |
|
||||
| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds |
|
||||
| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes |
|
||||
| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes |
|
||||
| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes |
|
||||
| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours |
|
||||
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
|
||||
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
|
||||
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
|
||||
| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours |
|
||||
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |
|
||||
|
||||
### State list lengths
|
||||
|
@ -305,29 +299,17 @@ class Validator(Container):
|
|||
withdrawable_epoch: Epoch # When validator can withdraw or transfer funds
|
||||
```
|
||||
|
||||
#### `Crosslink`
|
||||
|
||||
```python
|
||||
class Crosslink(Container):
|
||||
shard: Shard
|
||||
parent_root: Hash
|
||||
# Crosslinking data
|
||||
start_epoch: Epoch
|
||||
end_epoch: Epoch
|
||||
data_root: Hash
|
||||
```
|
||||
|
||||
#### `AttestationData`
|
||||
|
||||
```python
|
||||
class AttestationData(Container):
|
||||
slot: Slot
|
||||
index: CommitteeIndex
|
||||
# LMD GHOST vote
|
||||
beacon_block_root: Hash
|
||||
# FFG vote
|
||||
source: Checkpoint
|
||||
target: Checkpoint
|
||||
# Crosslink vote
|
||||
crosslink: Crosslink
|
||||
```
|
||||
|
||||
#### `AttestationDataAndCustodyBit`
|
||||
|
@ -335,7 +317,7 @@ class AttestationData(Container):
|
|||
```python
|
||||
class AttestationDataAndCustodyBit(Container):
|
||||
data: AttestationData
|
||||
custody_bit: bit # Challengeable bit (SSZ-bool, 1 byte) for the custody of crosslink data
|
||||
custody_bit: bit # Challengeable bit (SSZ-bool, 1 byte) for the custody of shard data
|
||||
```
|
||||
|
||||
#### `IndexedAttestation`
|
||||
|
@ -506,17 +488,13 @@ class BeaconState(Container):
|
|||
# Registry
|
||||
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
|
||||
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
|
||||
# Shuffling
|
||||
start_shard: Shard
|
||||
# Randomness
|
||||
randao_mixes: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR]
|
||||
# Slashings
|
||||
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
|
||||
# Attestations
|
||||
previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
|
||||
current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
|
||||
# Crosslinks
|
||||
previous_crosslinks: Vector[Crosslink, SHARD_COUNT] # Previous epoch snapshot
|
||||
current_crosslinks: Vector[Crosslink, SHARD_COUNT]
|
||||
# Finality
|
||||
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
|
||||
previous_justified_checkpoint: Checkpoint # Previous epoch snapshot
|
||||
|
@ -752,20 +730,20 @@ def compute_committee(indices: Sequence[ValidatorIndex],
|
|||
return [indices[compute_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)]
|
||||
```
|
||||
|
||||
#### `compute_epoch_of_slot`
|
||||
#### `compute_epoch_at_slot`
|
||||
|
||||
```python
|
||||
def compute_epoch_of_slot(slot: Slot) -> Epoch:
|
||||
def compute_epoch_at_slot(slot: Slot) -> Epoch:
|
||||
"""
|
||||
Return the epoch number of ``slot``.
|
||||
Return the epoch number at ``slot``.
|
||||
"""
|
||||
return Epoch(slot // SLOTS_PER_EPOCH)
|
||||
```
|
||||
|
||||
#### `compute_start_slot_of_epoch`
|
||||
#### `compute_start_slot_at_epoch`
|
||||
|
||||
```python
|
||||
def compute_start_slot_of_epoch(epoch: Epoch) -> Slot:
|
||||
def compute_start_slot_at_epoch(epoch: Epoch) -> Slot:
|
||||
"""
|
||||
Return the start slot of ``epoch``.
|
||||
"""
|
||||
|
@ -801,7 +779,7 @@ def get_current_epoch(state: BeaconState) -> Epoch:
|
|||
"""
|
||||
Return the current epoch.
|
||||
"""
|
||||
return compute_epoch_of_slot(state.slot)
|
||||
return compute_epoch_at_slot(state.slot)
|
||||
```
|
||||
|
||||
#### `get_previous_epoch`
|
||||
|
@ -822,7 +800,7 @@ def get_block_root(state: BeaconState, epoch: Epoch) -> Hash:
|
|||
"""
|
||||
Return the block root at the start of a recent ``epoch``.
|
||||
"""
|
||||
return get_block_root_at_slot(state, compute_start_slot_of_epoch(epoch))
|
||||
return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
||||
```
|
||||
|
||||
#### `get_block_root_at_slot`
|
||||
|
@ -878,61 +856,37 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Hash:
|
|||
return hash(domain_type + int_to_bytes(epoch, length=8) + mix)
|
||||
```
|
||||
|
||||
#### `get_committee_count`
|
||||
#### `get_committee_count_at_slot`
|
||||
|
||||
```python
|
||||
def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64:
|
||||
"""
|
||||
Return the number of committees at ``epoch``.
|
||||
Return the number of committees at ``slot``.
|
||||
"""
|
||||
committees_per_slot = max(1, min(
|
||||
SHARD_COUNT // SLOTS_PER_EPOCH,
|
||||
epoch = compute_epoch_at_slot(slot)
|
||||
return max(1, min(
|
||||
MAX_COMMITTEES_PER_SLOT,
|
||||
len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
))
|
||||
return committees_per_slot * SLOTS_PER_EPOCH
|
||||
```
|
||||
|
||||
#### `get_crosslink_committee`
|
||||
#### `get_beacon_committee`
|
||||
|
||||
```python
|
||||
def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
|
||||
def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
"""
|
||||
Return the crosslink committee at ``epoch`` for ``shard``.
|
||||
Return the beacon committee at ``slot`` for ``index``.
|
||||
"""
|
||||
epoch = compute_epoch_at_slot(slot)
|
||||
committees_per_slot = get_committee_count_at_slot(state, slot)
|
||||
return compute_committee(
|
||||
indices=get_active_validator_indices(state, epoch),
|
||||
seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
index=(shard + SHARD_COUNT - get_start_shard(state, epoch)) % SHARD_COUNT,
|
||||
count=get_committee_count(state, epoch),
|
||||
index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
||||
count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
)
|
||||
```
|
||||
|
||||
#### `get_start_shard`
|
||||
|
||||
```python
|
||||
def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
|
||||
"""
|
||||
Return the start shard of the 0th committee at ``epoch``.
|
||||
"""
|
||||
assert epoch <= get_current_epoch(state) + 1
|
||||
check_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
shard = Shard((state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT)
|
||||
while check_epoch > epoch:
|
||||
check_epoch -= Epoch(1)
|
||||
shard = Shard((shard + SHARD_COUNT - get_shard_delta(state, check_epoch)) % SHARD_COUNT)
|
||||
return shard
|
||||
```
|
||||
|
||||
#### `get_shard_delta`
|
||||
|
||||
```python
|
||||
def get_shard_delta(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
"""
|
||||
Return the number of shards to increment ``state.start_shard`` at ``epoch``.
|
||||
"""
|
||||
return min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)
|
||||
```
|
||||
|
||||
#### `get_beacon_proposer_index`
|
||||
|
||||
```python
|
||||
|
@ -946,18 +900,6 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
|||
return compute_proposer_index(state, indices, seed)
|
||||
```
|
||||
|
||||
#### `get_attestation_data_slot`
|
||||
|
||||
```python
|
||||
def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
|
||||
"""
|
||||
Return the slot corresponding to the attestation ``data``.
|
||||
"""
|
||||
committee_count = get_committee_count(state, data.target.epoch)
|
||||
offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT
|
||||
return Slot(compute_start_slot_of_epoch(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
|
||||
```
|
||||
|
||||
#### `get_total_balance`
|
||||
|
||||
```python
|
||||
|
@ -1019,7 +961,7 @@ def get_attesting_indices(state: BeaconState,
|
|||
"""
|
||||
Return the set of attesting indices corresponding to ``data`` and ``bits``.
|
||||
"""
|
||||
committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
return set(index for i, index in enumerate(committee) if bits[i])
|
||||
```
|
||||
|
||||
|
@ -1199,7 +1141,6 @@ def process_slot(state: BeaconState) -> None:
|
|||
```python
|
||||
def process_epoch(state: BeaconState) -> None:
|
||||
process_justification_and_finalization(state)
|
||||
process_crosslinks(state)
|
||||
process_rewards_and_penalties(state)
|
||||
process_registry_updates(state)
|
||||
# @process_reveal_deadlines
|
||||
|
@ -1230,7 +1171,7 @@ def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> Sequen
|
|||
def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
|
||||
return [
|
||||
a for a in get_matching_source_attestations(state, epoch)
|
||||
if a.data.beacon_block_root == get_block_root_at_slot(state, get_attestation_data_slot(state, a.data))
|
||||
if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot)
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -1248,23 +1189,6 @@ def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAtte
|
|||
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||
```
|
||||
|
||||
```python
|
||||
def get_winning_crosslink_and_attesting_indices(state: BeaconState,
|
||||
epoch: Epoch,
|
||||
shard: Shard) -> Tuple[Crosslink, Set[ValidatorIndex]]:
|
||||
attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard]
|
||||
crosslinks = filter(
|
||||
lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)),
|
||||
[a.data.crosslink for a in attestations]
|
||||
)
|
||||
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
|
||||
winning_crosslink = max(crosslinks, key=lambda c: (
|
||||
get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root
|
||||
), default=Crosslink())
|
||||
winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink]
|
||||
return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations)
|
||||
```
|
||||
|
||||
#### Justification and finalization
|
||||
|
||||
```python
|
||||
|
@ -1308,20 +1232,6 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
|||
state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
```
|
||||
|
||||
#### Crosslinks
|
||||
|
||||
```python
|
||||
def process_crosslinks(state: BeaconState) -> None:
|
||||
state.previous_crosslinks = [c for c in state.current_crosslinks]
|
||||
for epoch in (get_previous_epoch(state), get_current_epoch(state)):
|
||||
for offset in range(get_committee_count(state, epoch)):
|
||||
shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT)
|
||||
crosslink_committee = set(get_crosslink_committee(state, epoch, shard))
|
||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
||||
state.current_crosslinks[shard] = winning_crosslink
|
||||
```
|
||||
|
||||
#### Rewards and penalties
|
||||
|
||||
```python
|
||||
|
@ -1382,36 +1292,15 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence
|
|||
return rewards, penalties
|
||||
```
|
||||
|
||||
```python
|
||||
def get_crosslink_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
||||
epoch = get_previous_epoch(state)
|
||||
for offset in range(get_committee_count(state, epoch)):
|
||||
shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT)
|
||||
crosslink_committee = set(get_crosslink_committee(state, epoch, shard))
|
||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
attesting_balance = get_total_balance(state, attesting_indices)
|
||||
committee_balance = get_total_balance(state, crosslink_committee)
|
||||
for index in crosslink_committee:
|
||||
base_reward = get_base_reward(state, index)
|
||||
if index in attesting_indices:
|
||||
rewards[index] += base_reward * attesting_balance // committee_balance
|
||||
else:
|
||||
penalties[index] += base_reward
|
||||
return rewards, penalties
|
||||
```
|
||||
|
||||
```python
|
||||
def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||
return
|
||||
|
||||
rewards1, penalties1 = get_attestation_deltas(state)
|
||||
rewards2, penalties2 = get_crosslink_deltas(state)
|
||||
rewards, penalties = get_attestation_deltas(state)
|
||||
for index in range(len(state.validators)):
|
||||
increase_balance(state, ValidatorIndex(index), rewards1[index] + rewards2[index])
|
||||
decrease_balance(state, ValidatorIndex(index), penalties1[index] + penalties2[index])
|
||||
increase_balance(state, ValidatorIndex(index), rewards[index])
|
||||
decrease_balance(state, ValidatorIndex(index), penalties[index])
|
||||
```
|
||||
|
||||
#### Registry updates
|
||||
|
@ -1479,8 +1368,6 @@ def process_final_updates(state: BeaconState) -> None:
|
|||
if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
||||
historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots)
|
||||
state.historical_roots.append(hash_tree_root(historical_batch))
|
||||
# Update start shard
|
||||
state.start_shard = Shard((state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT)
|
||||
# Rotate current/previous epoch attestations
|
||||
state.previous_epoch_attestations = state.current_epoch_attestations
|
||||
state.current_epoch_attestations = []
|
||||
|
@ -1576,7 +1463,7 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla
|
|||
assert is_slashable_validator(proposer, get_current_epoch(state))
|
||||
# Signatures are valid
|
||||
for header in (proposer_slashing.header_1, proposer_slashing.header_2):
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_of_slot(header.slot))
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
|
||||
assert bls_verify(proposer.pubkey, signing_root(header), header.signature, domain)
|
||||
|
||||
slash_validator(state, proposer_slashing.proposer_index)
|
||||
|
@ -1607,37 +1494,27 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla
|
|||
```python
|
||||
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
data = attestation.data
|
||||
assert data.crosslink.shard < SHARD_COUNT
|
||||
assert data.index < get_committee_count_at_slot(state, data.slot)
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
|
||||
attestation_slot = get_attestation_data_slot(state, data)
|
||||
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||
|
||||
committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee)
|
||||
|
||||
pending_attestation = PendingAttestation(
|
||||
data=data,
|
||||
aggregation_bits=attestation.aggregation_bits,
|
||||
inclusion_delay=state.slot - attestation_slot,
|
||||
inclusion_delay=state.slot - data.slot,
|
||||
proposer_index=get_beacon_proposer_index(state),
|
||||
)
|
||||
|
||||
if data.target.epoch == get_current_epoch(state):
|
||||
assert data.source == state.current_justified_checkpoint
|
||||
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
|
||||
state.current_epoch_attestations.append(pending_attestation)
|
||||
else:
|
||||
assert data.source == state.previous_justified_checkpoint
|
||||
parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
|
||||
state.previous_epoch_attestations.append(pending_attestation)
|
||||
|
||||
# Check crosslink against expected parent crosslink
|
||||
assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
|
||||
assert data.crosslink.start_epoch == parent_crosslink.end_epoch
|
||||
assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
|
||||
assert data.crosslink.data_root == Bytes32() # [to be removed in phase 1]
|
||||
|
||||
# Check signature
|
||||
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
||||
```
|
||||
|
|
|
@ -118,7 +118,7 @@ def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei:
|
|||
def get_head(store: Store) -> Hash:
|
||||
# Execute the LMD-GHOST fork choice
|
||||
head = store.justified_checkpoint.root
|
||||
justified_slot = compute_start_slot_of_epoch(store.justified_checkpoint.epoch)
|
||||
justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
|
||||
while True:
|
||||
children = [
|
||||
root for root in store.blocks.keys()
|
||||
|
@ -156,7 +156,7 @@ def on_block(store: Store, block: BeaconBlock) -> None:
|
|||
store.finalized_checkpoint.root
|
||||
)
|
||||
# Check that block is later than the finalized epoch slot
|
||||
assert block.slot > compute_start_slot_of_epoch(store.finalized_checkpoint.epoch)
|
||||
assert block.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
# Check the block is valid and compute the post-state
|
||||
state = state_transition(pre_state, block)
|
||||
# Add new state for this block to the store
|
||||
|
@ -182,18 +182,17 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
|
|||
|
||||
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives
|
||||
base_state = store.block_states[target.root].copy()
|
||||
assert store.time >= base_state.genesis_time + compute_start_slot_of_epoch(target.epoch) * SECONDS_PER_SLOT
|
||||
assert store.time >= base_state.genesis_time + compute_start_slot_at_epoch(target.epoch) * SECONDS_PER_SLOT
|
||||
|
||||
# Store target checkpoint state if not yet seen
|
||||
if target not in store.checkpoint_states:
|
||||
process_slots(base_state, compute_start_slot_of_epoch(target.epoch))
|
||||
process_slots(base_state, compute_start_slot_at_epoch(target.epoch))
|
||||
store.checkpoint_states[target] = base_state
|
||||
target_state = store.checkpoint_states[target]
|
||||
|
||||
# Attestations can only affect the fork choice of subsequent slots.
|
||||
# Delay consideration in the fork choice until their slot is in the past.
|
||||
attestation_slot = get_attestation_data_slot(target_state, attestation.data)
|
||||
assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT
|
||||
assert store.time >= (attestation.data.slot + 1) * SECONDS_PER_SLOT
|
||||
|
||||
# Get state at the `target` to validate attestation and calculate the committees
|
||||
indexed_attestation = get_indexed_attestation(target_state, attestation)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
- [Configuration](#configuration)
|
||||
- [Containers](#containers)
|
||||
- [`CompactCommittee`](#compactcommittee)
|
||||
- [`ShardReceiptDelta`](#shardreceiptdelta)
|
||||
- [`ShardReceiptProof`](#shardreceiptproof)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [`pack_compact_validator`](#pack_compact_validator)
|
||||
|
@ -203,7 +204,7 @@ class BeaconState(Container):
|
|||
```
|
||||
|
||||
`period_committee_roots` values are initialized to `Bytes32()` (empty bytes value).
|
||||
`next_shard_receipt_period` values are initialized to `compute_epoch_of_slot(PHASE_1_FORK_SLOT) // EPOCHS_PER_SHARD_PERIOD`.
|
||||
`next_shard_receipt_period` values are initialized to `compute_epoch_at_slot(PHASE_1_FORK_SLOT) // EPOCHS_PER_SHARD_PERIOD`.
|
||||
|
||||
#### `BeaconBlockBody`
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
|||
| - | - |
|
||||
| `BLS12_381_Q` | `4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787` |
|
||||
| `MINOR_REWARD_QUOTIENT` | `2**8` (= 256) |
|
||||
| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours |
|
||||
|
||||
### Custody game parameters
|
||||
|
||||
|
@ -335,7 +336,7 @@ def legendre_bit(a: int, q: int) -> int:
|
|||
return 0
|
||||
```
|
||||
|
||||
### ```custody_subchunkify```
|
||||
### `custody_subchunkify`
|
||||
|
||||
Given one proof of custody chunk, returns the proof of custody subchunks of the correct sizes.
|
||||
|
||||
|
@ -546,7 +547,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge
|
|||
# Verify the attestation
|
||||
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, challenge.attestation))
|
||||
# Verify it is not too late to challenge
|
||||
assert (compute_epoch_of_slot(challenge.attestation.data.slot)
|
||||
assert (compute_epoch_at_slot(challenge.attestation.data.slot)
|
||||
>= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY)
|
||||
responder = state.validators[challenge.responder_index]
|
||||
assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY
|
||||
|
@ -623,7 +624,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) ->
|
|||
chunk_count = get_custody_chunk_count(attestation.data.crosslink)
|
||||
assert chunk_count == len(challenge.chunk_bits)
|
||||
# Verify custody bit is incorrect
|
||||
committee = get_crosslink_committee(state, epoch, shard)
|
||||
committee = get_beacon_committee(state, epoch, shard)
|
||||
custody_bit = attestation.custody_bits[committee.index(challenge.responder_index)]
|
||||
assert custody_bit != get_chunk_bits_root(challenge.chunk_bits)
|
||||
# Add new bit challenge record
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
- [Rewards and penalties](#rewards-and-penalties)
|
||||
- [Signature domain types](#signature-domain-types)
|
||||
- [Containers](#containers)
|
||||
- [`Crosslink`](#crosslink)
|
||||
- [`ShardBlock`](#shardblock)
|
||||
- [`ShardBlockHeader`](#shardblockheader)
|
||||
- [`ShardState`](#shardstate)
|
||||
- [`ShardAttestationData`](#ShardAttestationData)
|
||||
- [`ShardAttestationData`](#shardattestationdata)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Misc](#misc-1)
|
||||
- [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot)
|
||||
|
@ -53,6 +54,7 @@ This document describes the shard transition function (data layer only) and the
|
|||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `Shard` | `uint64` | a shard number |
|
||||
| `ShardSlot` | `uint64` | a shard slot number |
|
||||
|
||||
## Configuration
|
||||
|
@ -61,6 +63,7 @@ This document describes the shard transition function (data layer only) and the
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `SHARD_COUNT` | `2**10` (= 1,024) |
|
||||
| `MIN_BLOCK_BODY_PRICE` | `2**0` (= 1) |
|
||||
| `MAX_PERIOD_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `SHARD_HEADER_SIZE` | `2**10` (= 1024) |
|
||||
|
@ -101,6 +104,14 @@ This document describes the shard transition function (data layer only) and the
|
|||
|
||||
## Containers
|
||||
|
||||
### `Crosslink`
|
||||
|
||||
```python
|
||||
# Crosslink is a placeholder to appease the build script until phase 1 is reworked
|
||||
class Crosslink(Container):
|
||||
shard: Shard
|
||||
```
|
||||
|
||||
### `ShardBlock`
|
||||
|
||||
```python
|
||||
|
|
|
@ -84,7 +84,7 @@ def get_persistent_committee_pubkeys_and_balances(memory: LightClientMemory,
|
|||
"""
|
||||
Return pubkeys and balances for the persistent committee at ``epoch``.
|
||||
"""
|
||||
current_period = compute_epoch_of_slot(memory.header.slot) // EPOCHS_PER_SHARD_PERIOD
|
||||
current_period = compute_epoch_at_slot(memory.header.slot) // EPOCHS_PER_SHARD_PERIOD
|
||||
next_period = epoch // EPOCHS_PER_SHARD_PERIOD
|
||||
assert next_period in (current_period, current_period + 1)
|
||||
if next_period == current_period:
|
||||
|
@ -114,7 +114,7 @@ The state of a light client is stored in a `memory` object of type `LightClientM
|
|||
```python
|
||||
def update_memory(memory: LightClientMemory, update: LightClientUpdate) -> None:
|
||||
# Verify the update does not skip a period
|
||||
current_period = compute_epoch_of_slot(memory.header.slot) // EPOCHS_PER_SHARD_PERIOD
|
||||
current_period = compute_epoch_at_slot(memory.header.slot) // EPOCHS_PER_SHARD_PERIOD
|
||||
next_epoch = compute_epoch_of_shard_slot(update.header.slot)
|
||||
next_period = next_epoch // EPOCHS_PER_SHARD_PERIOD
|
||||
assert next_period in (current_period, current_period + 1)
|
||||
|
|
|
@ -38,7 +38,6 @@ The provided pre-state is already transitioned to just before the specific sub-t
|
|||
Sub-transitions:
|
||||
|
||||
- `justification_and_finalization`
|
||||
- `crosslinks`
|
||||
- *`rewards_and_penalties` - planned testing extension*
|
||||
- `registry_updates`
|
||||
- `slashings`
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
- [Attestation data](#attestation-data)
|
||||
- [LMD GHOST vote](#lmd-ghost-vote)
|
||||
- [FFG vote](#ffg-vote)
|
||||
- [Crosslink vote](#crosslink-vote)
|
||||
- [Construct attestation](#construct-attestation)
|
||||
- [Data](#data)
|
||||
- [Aggregation bits](#aggregation-bits)
|
||||
|
@ -136,28 +135,25 @@ A validator can get committee assignments for a given epoch using the following
|
|||
```python
|
||||
def get_committee_assignment(state: BeaconState,
|
||||
epoch: Epoch,
|
||||
validator_index: ValidatorIndex) -> Optional[Tuple[Sequence[ValidatorIndex], Shard, Slot]]:
|
||||
validator_index: ValidatorIndex
|
||||
) -> Optional[Tuple[Sequence[ValidatorIndex], CommitteeIndex, Slot]]:
|
||||
"""
|
||||
Return the committee assignment in the ``epoch`` for ``validator_index``.
|
||||
``assignment`` returned is a tuple of the following form:
|
||||
* ``assignment[0]`` is the list of validators in the committee
|
||||
* ``assignment[1]`` is the shard to which the committee is assigned
|
||||
* ``assignment[1]`` is the index to which the committee is assigned
|
||||
* ``assignment[2]`` is the slot at which the committee is assigned
|
||||
Return None if no assignment.
|
||||
"""
|
||||
next_epoch = get_current_epoch(state) + 1
|
||||
assert epoch <= next_epoch
|
||||
|
||||
committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||
start_slot = compute_start_slot_of_epoch(epoch)
|
||||
start_slot = compute_start_slot_at_epoch(epoch)
|
||||
for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
|
||||
offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
|
||||
slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT
|
||||
for i in range(committees_per_slot):
|
||||
shard = Shard((slot_start_shard + i) % SHARD_COUNT)
|
||||
committee = get_crosslink_committee(state, epoch, shard)
|
||||
for index in range(get_committee_count_at_slot(state, Slot(slot))):
|
||||
committee = get_beacon_committee(state, Slot(slot), CommitteeIndex(index))
|
||||
if validator_index in committee:
|
||||
return committee, shard, Slot(slot)
|
||||
return committee, CommitteeIndex(index), Slot(slot)
|
||||
return None
|
||||
```
|
||||
|
||||
|
@ -171,13 +167,13 @@ def is_proposer(state: BeaconState,
|
|||
|
||||
*Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot.
|
||||
|
||||
*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsibility might occur at different a different slot.
|
||||
*Note*: `BeaconBlock` proposal is distinct from beacon committee assignment, and in a given epoch each responsibility might occur at different a different slot.
|
||||
|
||||
### Lookahead
|
||||
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
|
||||
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest and also which shard they should begin syncing (in Phase 1+).
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest.
|
||||
|
||||
Specifically, a validator should call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
|
||||
|
||||
|
@ -215,8 +211,8 @@ Set `block.randao_reveal = epoch_signature` where `epoch_signature` is obtained
|
|||
|
||||
```python
|
||||
def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature:
|
||||
domain = get_domain(state, DOMAIN_RANDAO, compute_epoch_of_slot(block.slot))
|
||||
return bls_sign(privkey, hash_tree_root(compute_epoch_of_slot(block.slot)), domain)
|
||||
domain = get_domain(state, DOMAIN_RANDAO, compute_epoch_at_slot(block.slot))
|
||||
return bls_sign(privkey, hash_tree_root(compute_epoch_at_slot(block.slot)), domain)
|
||||
```
|
||||
|
||||
##### Eth1 Data
|
||||
|
@ -249,7 +245,7 @@ Set `header.signature = block_signature` where `block_signature` is obtained fro
|
|||
|
||||
```python
|
||||
def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature:
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_of_slot(header.slot))
|
||||
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
|
||||
return bls_sign(privkey, signing_root(header), domain)
|
||||
```
|
||||
|
||||
|
@ -279,7 +275,7 @@ Up to `MAX_VOLUNTARY_EXITS`, [`VoluntaryExit`](../core/0_beacon-chain.md#volunta
|
|||
|
||||
### Attestations
|
||||
|
||||
A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `shard`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`.
|
||||
A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `index`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`.
|
||||
|
||||
A validator should create and broadcast the attestation halfway through the `slot` during which the validator is assigned―that is, `SECONDS_PER_SLOT * 0.5` seconds after the start of `slot`.
|
||||
|
||||
|
@ -290,6 +286,11 @@ First, the validator should construct `attestation_data`, an [`AttestationData`]
|
|||
- Let `head_block` be the result of running the fork choice during the assigned slot.
|
||||
- Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot using `process_slots(state, slot)`.
|
||||
|
||||
##### General
|
||||
|
||||
* Set `attestation_data.slot = slot` where `slot` is the assigned slot.
|
||||
* Set `attestation_data.index = index` where `index` is the index associated with the validator's committee.
|
||||
|
||||
##### LMD GHOST vote
|
||||
|
||||
Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
||||
|
@ -301,20 +302,9 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
|||
|
||||
*Note*: `epoch_boundary_block_root` can be looked up in the state using:
|
||||
|
||||
- Let `start_slot = compute_start_slot_of_epoch(get_current_epoch(head_state))`.
|
||||
- Let `start_slot = compute_start_slot_at_epoch(get_current_epoch(head_state))`.
|
||||
- Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
|
||||
|
||||
##### Crosslink vote
|
||||
|
||||
Construct `attestation_data.crosslink` via the following.
|
||||
|
||||
- Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee.
|
||||
- Let `parent_crosslink = head_state.current_crosslinks[shard]`.
|
||||
- Set `attestation_data.crosslink.start_epoch = parent_crosslink.end_epoch`.
|
||||
- Set `attestation_data.crosslink.end_epoch = min(attestation_data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)`.
|
||||
- Set `attestation_data.crosslink.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`.
|
||||
- Set `attestation_data.crosslink.data_root = ZERO_HASH`. *Note*: This is a stub for Phase 0.
|
||||
|
||||
#### Construct attestation
|
||||
|
||||
Next, the validator creates `attestation`, an [`Attestation`](../core/0_beacon-chain.md#attestation) object.
|
||||
|
@ -364,7 +354,7 @@ To avoid "proposer slashings", a validator must not sign two conflicting [`Beaco
|
|||
|
||||
Specifically, when signing a `BeaconBlock`, a validator should perform the following steps in the following order:
|
||||
|
||||
1. Save a record to hard disk that a beacon block has been signed for the `epoch=compute_epoch_of_slot(block.slot)`.
|
||||
1. Save a record to hard disk that a beacon block has been signed for the `epoch=compute_epoch_at_slot(block.slot)`.
|
||||
2. Generate and broadcast the block.
|
||||
|
||||
If the software crashes at some point within this routine, then when the validator comes back online, the hard disk has the record of the *potentially* signed/broadcast block and can effectively avoid slashing.
|
||||
|
|
|
@ -3,7 +3,6 @@ from typing import Iterable
|
|||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.phase_0.epoch_processing import (
|
||||
test_process_crosslinks,
|
||||
test_process_final_updates,
|
||||
test_process_justification_and_finalization,
|
||||
test_process_registry_updates,
|
||||
|
@ -35,8 +34,6 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
|||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("epoch_processing", [
|
||||
create_provider('crosslinks', test_process_crosslinks, 'minimal'),
|
||||
create_provider('crosslinks', test_process_crosslinks, 'mainnet'),
|
||||
create_provider('final_updates', test_process_final_updates, 'minimal'),
|
||||
create_provider('final_updates', test_process_final_updates, 'mainnet'),
|
||||
create_provider('justification_and_finalization', test_process_justification_and_finalization, 'minimal'),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
# from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
from .helpers.genesis import create_genesis_state
|
||||
|
@ -137,7 +137,9 @@ def with_phases(phases):
|
|||
if 'phase0' in run_phases:
|
||||
ret = run_with_spec_version(spec_phase0, *args, **kw)
|
||||
if 'phase1' in run_phases:
|
||||
ret = run_with_spec_version(spec_phase1, *args, **kw)
|
||||
# temporarily disable phase 1 tests
|
||||
return
|
||||
# ret = run_with_spec_version(spec_phase1, *args, **kw)
|
||||
return ret
|
||||
return wrapper
|
||||
return decorator
|
||||
|
|
|
@ -3,11 +3,10 @@ from typing import List
|
|||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
||||
|
||||
|
||||
def build_attestation_data(spec, state, slot, shard):
|
||||
def build_attestation_data(spec, state, slot, index):
|
||||
assert state.slot >= slot
|
||||
|
||||
if slot == state.slot:
|
||||
|
@ -15,7 +14,7 @@ def build_attestation_data(spec, state, slot, shard):
|
|||
else:
|
||||
block_root = spec.get_block_root_at_slot(state, slot)
|
||||
|
||||
current_epoch_start_slot = spec.compute_start_slot_of_epoch(spec.get_current_epoch(state))
|
||||
current_epoch_start_slot = spec.compute_start_slot_at_epoch(spec.get_current_epoch(state))
|
||||
if slot < current_epoch_start_slot:
|
||||
epoch_boundary_root = spec.get_block_root(state, spec.get_previous_epoch(state))
|
||||
elif slot == current_epoch_start_slot:
|
||||
|
@ -30,43 +29,30 @@ def build_attestation_data(spec, state, slot, shard):
|
|||
source_epoch = state.current_justified_checkpoint.epoch
|
||||
source_root = state.current_justified_checkpoint.root
|
||||
|
||||
if spec.compute_epoch_of_slot(slot) == spec.get_current_epoch(state):
|
||||
parent_crosslink = state.current_crosslinks[shard]
|
||||
else:
|
||||
parent_crosslink = state.previous_crosslinks[shard]
|
||||
|
||||
return spec.AttestationData(
|
||||
slot=slot,
|
||||
index=index,
|
||||
beacon_block_root=block_root,
|
||||
source=spec.Checkpoint(epoch=source_epoch, root=source_root),
|
||||
target=spec.Checkpoint(epoch=spec.compute_epoch_of_slot(slot), root=epoch_boundary_root),
|
||||
crosslink=spec.Crosslink(
|
||||
shard=shard,
|
||||
start_epoch=parent_crosslink.end_epoch,
|
||||
end_epoch=min(spec.compute_epoch_of_slot(slot), parent_crosslink.end_epoch + spec.MAX_EPOCHS_PER_CROSSLINK),
|
||||
data_root=spec.Hash(),
|
||||
parent_root=hash_tree_root(parent_crosslink),
|
||||
),
|
||||
target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root),
|
||||
)
|
||||
|
||||
|
||||
def get_valid_attestation(spec, state, slot=None, signed=False):
|
||||
def get_valid_attestation(spec, state, slot=None, index=None, signed=False):
|
||||
if slot is None:
|
||||
slot = state.slot
|
||||
if index is None:
|
||||
index = 0
|
||||
|
||||
epoch = spec.compute_epoch_of_slot(slot)
|
||||
epoch_start_shard = spec.get_start_shard(state, epoch)
|
||||
committees_per_slot = spec.get_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH
|
||||
shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT
|
||||
attestation_data = build_attestation_data(spec, state, slot, index)
|
||||
|
||||
attestation_data = build_attestation_data(spec, state, slot, shard)
|
||||
|
||||
crosslink_committee = spec.get_crosslink_committee(
|
||||
beacon_committee = spec.get_beacon_committee(
|
||||
state,
|
||||
attestation_data.target.epoch,
|
||||
attestation_data.crosslink.shard,
|
||||
attestation_data.slot,
|
||||
attestation_data.index,
|
||||
)
|
||||
|
||||
committee_size = len(crosslink_committee)
|
||||
committee_size = len(beacon_committee)
|
||||
aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size))
|
||||
custody_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size))
|
||||
attestation = spec.Attestation(
|
||||
|
@ -129,12 +115,12 @@ def get_attestation_signature(spec, state, attestation_data, privkey, custody_bi
|
|||
|
||||
|
||||
def fill_aggregate_attestation(spec, state, attestation):
|
||||
crosslink_committee = spec.get_crosslink_committee(
|
||||
beacon_committee = spec.get_beacon_committee(
|
||||
state,
|
||||
attestation.data.target.epoch,
|
||||
attestation.data.crosslink.shard,
|
||||
attestation.data.slot,
|
||||
attestation.data.index,
|
||||
)
|
||||
for i in range(len(crosslink_committee)):
|
||||
for i in range(len(beacon_committee)):
|
||||
attestation.aggregation_bits[i] = True
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ def sign_block(spec, state, block, proposer_index=None):
|
|||
if block.slot == state.slot:
|
||||
proposer_index = spec.get_beacon_proposer_index(state)
|
||||
else:
|
||||
if spec.compute_epoch_of_slot(state.slot) + 1 > spec.compute_epoch_of_slot(block.slot):
|
||||
if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(block.slot):
|
||||
print("warning: block slot far away, and no proposer index manually given."
|
||||
" Signing block is slow due to transition for proposer index calculation.")
|
||||
# use stub state to get proposer index of future slot
|
||||
|
@ -26,10 +26,10 @@ def sign_block(spec, state, block, proposer_index=None):
|
|||
|
||||
block.body.randao_reveal = bls_sign(
|
||||
privkey=privkey,
|
||||
message_hash=hash_tree_root(spec.compute_epoch_of_slot(block.slot)),
|
||||
message_hash=hash_tree_root(spec.compute_epoch_at_slot(block.slot)),
|
||||
domain=spec.get_domain(
|
||||
state,
|
||||
message_epoch=spec.compute_epoch_of_slot(block.slot),
|
||||
message_epoch=spec.compute_epoch_at_slot(block.slot),
|
||||
domain_type=spec.DOMAIN_RANDAO,
|
||||
)
|
||||
)
|
||||
|
@ -39,7 +39,7 @@ def sign_block(spec, state, block, proposer_index=None):
|
|||
domain=spec.get_domain(
|
||||
state,
|
||||
spec.DOMAIN_BEACON_PROPOSER,
|
||||
spec.compute_epoch_of_slot(block.slot)))
|
||||
spec.compute_epoch_at_slot(block.slot)))
|
||||
|
||||
|
||||
def apply_empty_block(spec, state):
|
||||
|
|
|
@ -80,13 +80,13 @@ def bitlist_from_int(max_len, num_bits, n):
|
|||
|
||||
|
||||
def get_valid_bit_challenge(spec, state, attestation, invalid_custody_bit=False):
|
||||
crosslink_committee = spec.get_crosslink_committee(
|
||||
beacon_committee = spec.get_beacon_committee(
|
||||
state,
|
||||
attestation.data.target.epoch,
|
||||
attestation.data.slot,
|
||||
attestation.data.crosslink.shard,
|
||||
)
|
||||
responder_index = crosslink_committee[0]
|
||||
challenger_index = crosslink_committee[-1]
|
||||
responder_index = beacon_committee[0]
|
||||
challenger_index = beacon_committee[-1]
|
||||
|
||||
epoch = spec.get_randao_epoch_for_custody_period(attestation.data.target.epoch,
|
||||
responder_index)
|
||||
|
|
|
@ -52,14 +52,18 @@ def next_epoch_with_attestations(spec,
|
|||
block = build_empty_block_for_next_slot(spec, post_state)
|
||||
if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
|
||||
if slot_to_attest >= spec.compute_start_slot_of_epoch(spec.get_current_epoch(post_state)):
|
||||
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest)
|
||||
block.body.attestations.append(cur_attestation)
|
||||
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
|
||||
if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
|
||||
for index in range(committees_per_slot):
|
||||
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
|
||||
block.body.attestations.append(cur_attestation)
|
||||
|
||||
if fill_prev_epoch:
|
||||
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
|
||||
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest)
|
||||
block.body.attestations.append(prev_attestation)
|
||||
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
|
||||
for index in range(committees_per_slot):
|
||||
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
|
||||
block.body.attestations.append(prev_attestation)
|
||||
|
||||
state_transition_and_sign_block(spec, post_state, block)
|
||||
blocks.append(block)
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
expect_assertion_error,
|
||||
always_bls, never_bls,
|
||||
with_all_phases, with_phases,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_attestation,
|
||||
sign_aggregate_attestation,
|
||||
|
@ -6,7 +11,6 @@ from eth2spec.test.helpers.attestations import (
|
|||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.block import apply_empty_block
|
||||
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
||||
|
@ -67,54 +71,6 @@ def test_success_previous_epoch(spec, state):
|
|||
yield from run_attestation_processing(spec, state, attestation)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_success_since_max_epochs_per_crosslink(spec, state):
|
||||
# Do not run mainnet (64 epochs), that would mean the equivalent of ~7 hours chain simulation.
|
||||
if spec.MAX_EPOCHS_PER_CROSSLINK > 4:
|
||||
return
|
||||
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=True)
|
||||
data = attestation.data
|
||||
# test logic sanity check: make sure the attestation only includes MAX_EPOCHS_PER_CROSSLINK epochs
|
||||
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
|
||||
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_wrong_end_epoch_with_max_epochs_per_crosslink(spec, state):
|
||||
# Do not run mainnet (64 epochs), that would mean the equivalent of ~7 hours chain simulation.
|
||||
if spec.MAX_EPOCHS_PER_CROSSLINK > 4:
|
||||
return
|
||||
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
data = attestation.data
|
||||
# test logic sanity check: make sure the attestation only includes MAX_EPOCHS_PER_CROSSLINK epochs
|
||||
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
|
||||
# Now change it to be different
|
||||
data.crosslink.end_epoch += 1
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
|
@ -168,27 +124,41 @@ def test_old_source_epoch(spec, state):
|
|||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_wrong_shard(spec, state):
|
||||
@always_bls
|
||||
def test_wrong_index_for_committee_signature(spec, state):
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.crosslink.shard += 1
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
attestation.data.index += 1
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_invalid_shard(spec, state):
|
||||
@never_bls
|
||||
def test_wrong_index_for_slot(spec, state):
|
||||
committees_per_slot = spec.get_committee_count_at_slot(state, state.slot)
|
||||
assert committees_per_slot < spec.MAX_COMMITTEES_PER_SLOT
|
||||
index = committees_per_slot
|
||||
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.index = index
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_invalid_index(spec, state):
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
# off by one (with respect to valid range) on purpose
|
||||
attestation.data.crosslink.shard = spec.SHARD_COUNT
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
@ -290,73 +260,6 @@ def test_bad_source_root(spec, state):
|
|||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spec_state_test
|
||||
def test_non_zero_crosslink_data_root(spec, state):
|
||||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.crosslink.data_root = b'\x42' * 32
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_bad_parent_crosslink(spec, state):
|
||||
state.slot = spec.SLOTS_PER_EPOCH - 1
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=False)
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation.data.crosslink.parent_root = b'\x27' * 32
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_bad_crosslink_start_epoch(spec, state):
|
||||
state.slot = spec.SLOTS_PER_EPOCH - 1
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=False)
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation.data.crosslink.start_epoch += 1
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_bad_crosslink_end_epoch(spec, state):
|
||||
state.slot = spec.SLOTS_PER_EPOCH - 1
|
||||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=False)
|
||||
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
attestation.data.crosslink.end_epoch += 1
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
yield from run_attestation_processing(spec, state, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_inconsistent_bits(spec, state):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
process_calls = [
|
||||
'process_justification_and_finalization',
|
||||
'process_crosslinks',
|
||||
'process_rewards_and_penalties',
|
||||
'process_registry_updates',
|
||||
'process_reveal_deadlines',
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_slot
|
||||
)
|
||||
from eth2spec.test.helpers.block import apply_empty_block
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
add_attestation_to_state,
|
||||
fill_aggregate_attestation,
|
||||
get_valid_attestation,
|
||||
sign_attestation,
|
||||
)
|
||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
|
||||
|
||||
def run_process_crosslinks(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_crosslinks')
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_attestations(spec, state):
|
||||
yield from run_process_crosslinks(spec, state)
|
||||
|
||||
for shard in range(spec.SHARD_COUNT):
|
||||
assert state.previous_crosslinks[shard] == state.current_crosslinks[shard]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_single_crosslink_update_from_current_epoch(spec, state):
|
||||
next_epoch(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=True)
|
||||
|
||||
fill_aggregate_attestation(spec, state, attestation)
|
||||
add_attestation_to_state(spec, state, attestation, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||
|
||||
assert len(state.current_epoch_attestations) == 1
|
||||
|
||||
shard = attestation.data.crosslink.shard
|
||||
pre_crosslink = deepcopy(state.current_crosslinks[shard])
|
||||
|
||||
yield from run_process_crosslinks(spec, state)
|
||||
|
||||
assert state.previous_crosslinks[shard] != state.current_crosslinks[shard]
|
||||
assert pre_crosslink != state.current_crosslinks[shard]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_single_crosslink_update_from_previous_epoch(spec, state):
|
||||
next_epoch(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, signed=True)
|
||||
|
||||
fill_aggregate_attestation(spec, state, attestation)
|
||||
add_attestation_to_state(spec, state, attestation, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
assert len(state.previous_epoch_attestations) == 1
|
||||
|
||||
shard = attestation.data.crosslink.shard
|
||||
pre_crosslink = deepcopy(state.current_crosslinks[shard])
|
||||
|
||||
crosslink_deltas = spec.get_crosslink_deltas(state)
|
||||
|
||||
yield from run_process_crosslinks(spec, state)
|
||||
|
||||
assert state.previous_crosslinks[shard] != state.current_crosslinks[shard]
|
||||
assert pre_crosslink != state.current_crosslinks[shard]
|
||||
|
||||
# ensure rewarded
|
||||
for index in spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation.data.target.epoch,
|
||||
attestation.data.crosslink.shard):
|
||||
assert crosslink_deltas[0][index] > 0
|
||||
assert crosslink_deltas[1][index] == 0
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_late_crosslink(spec, state):
|
||||
if spec.get_committee_count(state, spec.get_current_epoch(state)) < spec.SHARD_COUNT:
|
||||
print("warning: ignoring test, test-assumptions are incompatible with configuration")
|
||||
return
|
||||
|
||||
next_epoch(spec, state)
|
||||
state.slot += 4
|
||||
|
||||
attestation_1 = get_valid_attestation(spec, state, signed=True)
|
||||
fill_aggregate_attestation(spec, state, attestation_1)
|
||||
|
||||
# add attestation_1 to next epoch
|
||||
next_epoch(spec, state)
|
||||
add_attestation_to_state(spec, state, attestation_1, state.slot + 1)
|
||||
|
||||
for _ in range(spec.SLOTS_PER_EPOCH):
|
||||
attestation_2 = get_valid_attestation(spec, state)
|
||||
if attestation_2.data.crosslink.shard == attestation_1.data.crosslink.shard:
|
||||
sign_attestation(spec, state, attestation_2)
|
||||
break
|
||||
next_slot(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
fill_aggregate_attestation(spec, state, attestation_2)
|
||||
|
||||
# add attestation_2 in the next epoch after attestation_1 has
|
||||
# already updated the relevant crosslink
|
||||
next_epoch(spec, state)
|
||||
add_attestation_to_state(spec, state, attestation_2, state.slot + 1)
|
||||
|
||||
assert len(state.previous_epoch_attestations) == 1
|
||||
assert len(state.current_epoch_attestations) == 0
|
||||
|
||||
crosslink_deltas = spec.get_crosslink_deltas(state)
|
||||
|
||||
yield from run_process_crosslinks(spec, state)
|
||||
|
||||
shard = attestation_2.data.crosslink.shard
|
||||
|
||||
# ensure that the current crosslinks were not updated by the second attestation
|
||||
assert state.previous_crosslinks[shard] == state.current_crosslinks[shard]
|
||||
# ensure no reward, only penalties for the failed crosslink
|
||||
for index in spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation_2.data.target.epoch,
|
||||
attestation_2.data.crosslink.shard):
|
||||
assert crosslink_deltas[0][index] == 0
|
||||
assert crosslink_deltas[1][index] > 0
|
|
@ -8,14 +8,6 @@ def run_process_just_and_fin(spec, state):
|
|||
yield from run_epoch_processing_with(spec, state, 'process_justification_and_finalization')
|
||||
|
||||
|
||||
def get_shards_for_slot(spec, state, slot):
|
||||
epoch = spec.compute_epoch_of_slot(slot)
|
||||
epoch_start_shard = spec.get_start_shard(state, epoch)
|
||||
committees_per_slot = spec.get_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH
|
||||
shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT
|
||||
return [shard + i for i in range(committees_per_slot)]
|
||||
|
||||
|
||||
def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False):
|
||||
# we must be at the end of the epoch
|
||||
assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0
|
||||
|
@ -33,15 +25,16 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
|
|||
total_balance = spec.get_total_active_balance(state)
|
||||
remaining_balance = total_balance * 2 // 3
|
||||
|
||||
start_slot = spec.compute_start_slot_of_epoch(epoch)
|
||||
start_slot = spec.compute_start_slot_at_epoch(epoch)
|
||||
for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH):
|
||||
for shard in get_shards_for_slot(spec, state, slot):
|
||||
committees_per_slot = spec.get_committee_count_at_slot(state, slot)
|
||||
for index in range(committees_per_slot):
|
||||
# Check if we already have had sufficient balance. (and undone if we don't want it).
|
||||
# If so, do not create more attestations. (we do not have empty pending attestations normally anyway)
|
||||
if remaining_balance < 0:
|
||||
return
|
||||
|
||||
committee = spec.get_crosslink_committee(state, spec.compute_epoch_of_slot(slot), shard)
|
||||
committee = spec.get_beacon_committee(state, slot, index)
|
||||
# Create a bitfield filled with the given count per attestation,
|
||||
# exactly on the right-most part of the committee field.
|
||||
|
||||
|
@ -60,10 +53,11 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
|
|||
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,
|
||||
crosslink=spec.Crosslink(shard=shard)
|
||||
index=index,
|
||||
),
|
||||
inclusion_delay=1,
|
||||
))
|
||||
|
@ -80,7 +74,7 @@ def get_checkpoints(spec, epoch):
|
|||
|
||||
def put_checkpoints_in_block_roots(spec, state, checkpoints):
|
||||
for c in checkpoints:
|
||||
state.block_roots[spec.compute_start_slot_of_epoch(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root
|
||||
state.block_roots[spec.compute_start_slot_at_epoch(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root
|
||||
|
||||
|
||||
def finalize_on_234(spec, state, epoch, sufficient_support):
|
||||
|
|
Loading…
Reference in New Issue