initial removal and cleanup of shard/crosslink from phase 0
This commit is contained in:
parent
fffdb24708
commit
dfdf3ab5cf
|
@ -25,7 +25,6 @@
|
|||
- [`Fork`](#fork)
|
||||
- [`Checkpoint`](#checkpoint)
|
||||
- [`Validator`](#validator)
|
||||
- [`Crosslink`](#crosslink)
|
||||
- [`AttestationData`](#attestationdata)
|
||||
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
|
||||
- [`IndexedAttestation`](#indexedattestation)
|
||||
|
@ -84,8 +83,6 @@
|
|||
- [`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_beacon_proposer_index`](#get_beacon_proposer_index)
|
||||
- [`get_attestation_data_slot`](#get_attestation_data_slot)
|
||||
- [`get_total_balance`](#get_total_balance)
|
||||
|
@ -105,7 +102,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)
|
||||
|
@ -174,6 +170,7 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `COMMITTEES_PER_SLOT` | `2**5` (= 32) |
|
||||
| `SHARD_COUNT` | `2**10` (= 1,024) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**12` (= 4,096) |
|
||||
|
@ -305,29 +302,18 @@ 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
|
||||
# LMD GHOST vote
|
||||
beacon_block_root: Hash
|
||||
# FFG vote
|
||||
source: Checkpoint
|
||||
target: Checkpoint
|
||||
# Crosslink vote
|
||||
crosslink: Crosslink
|
||||
# Index -- Maybe remove
|
||||
index: uint64
|
||||
```
|
||||
|
||||
#### `AttestationDataAndCustodyBit`
|
||||
|
@ -507,16 +493,12 @@ class BeaconState(Container):
|
|||
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
|
||||
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
|
||||
# Shuffling
|
||||
start_shard: Shard
|
||||
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
|
||||
|
@ -885,54 +867,32 @@ def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64:
|
|||
"""
|
||||
Return the number of committees at ``epoch``.
|
||||
"""
|
||||
# Consider not hard coding but just return committees per slot for now
|
||||
"""
|
||||
committees_per_slot = max(1, min(
|
||||
SHARD_COUNT // SLOTS_PER_EPOCH,
|
||||
len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
))
|
||||
return committees_per_slot * SLOTS_PER_EPOCH
|
||||
"""
|
||||
return COMMITTEES_PER_SLOT
|
||||
```
|
||||
|
||||
#### `get_crosslink_committee`
|
||||
|
||||
```python
|
||||
def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
|
||||
def get_crosslink_committee(state: BeaconState, epoch: Epoch, index: uint64) -> Sequence[ValidatorIndex]:
|
||||
"""
|
||||
Return the crosslink committee at ``epoch`` for ``shard``.
|
||||
Return the crosslink committee at ``epoch`` for ``index``.
|
||||
"""
|
||||
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,
|
||||
index=index,
|
||||
count=get_committee_count(state, 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 +906,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 +967,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_crosslink_committee(state, data.target.epoch, data.index)
|
||||
return set(index for i, index in enumerate(committee) if bits[i])
|
||||
```
|
||||
|
||||
|
@ -1199,7 +1147,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 +1177,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 +1195,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 +1238,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
|
||||
|
@ -1384,36 +1300,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)
|
||||
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), rewards1[index])
|
||||
decrease_balance(state, ValidatorIndex(index), penalties1[index])
|
||||
```
|
||||
|
||||
#### Registry updates
|
||||
|
@ -1481,8 +1376,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 = []
|
||||
|
@ -1609,37 +1502,28 @@ 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 < COMMITTEES_PER_SLOT
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
|
||||
attestation_slot = get_attestation_data_slot(state, data)
|
||||
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
|
||||
committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
committee = get_crosslink_committee(state, data.target.epoch, 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))
|
||||
```
|
||||
|
|
|
@ -192,7 +192,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
|
|||
|
||||
# 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)
|
||||
attestation_slot = attestation.data.slot
|
||||
assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT
|
||||
|
||||
# Get state at the `target` to validate attestation and calculate the committees
|
||||
|
|
|
@ -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)
|
||||
|
@ -135,28 +134,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], uint64, 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)
|
||||
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(COMMITTEES_PER_SLOT):
|
||||
committee = get_crosslink_committee(state, epoch, index)
|
||||
if validator_index in committee:
|
||||
return committee, shard, Slot(slot)
|
||||
return committee, index, Slot(slot)
|
||||
return None
|
||||
```
|
||||
|
||||
|
@ -176,7 +172,7 @@ def is_proposer(state: BeaconState,
|
|||
|
||||
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.
|
||||
|
||||
|
@ -278,7 +274,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`.
|
||||
|
||||
|
@ -303,16 +299,9 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
|||
- Let `start_slot = compute_start_slot_of_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
|
||||
##### Index
|
||||
|
||||
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.
|
||||
Set `attestation_data.index = index` where `index` is the index associated with the validator's committee.
|
||||
|
||||
#### Construct attestation
|
||||
|
||||
|
|
Loading…
Reference in New Issue