Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Shards/Crosslinks from Phase 0 #1428

Merged
merged 24 commits into from
Oct 28, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions configs/mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

# Misc
# ---------------------------------------------------------------
# 2**10 (= 1,024)
SHARD_COUNT: 1024
# 2**5 (= 32)
COMMITTEES_PER_SLOT: 32
# 2**7 (= 128)
TARGET_COMMITTEE_SIZE: 128
# 2**12 (= 4,096)
Expand Down
4 changes: 2 additions & 2 deletions configs/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# Misc
# ---------------------------------------------------------------

# [customized] Just 8 shards for testing purposes
SHARD_COUNT: 8
# [customized] Just 2 committees for slot for testing purposes
COMMITTEES_PER_SLOT: 2
# [customized] unsecure, but fast
TARGET_COMMITTEE_SIZE: 4
# 2**12 (= 4,096)
Expand Down
170 changes: 17 additions & 153 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
- [`Fork`](#fork)
- [`Checkpoint`](#checkpoint)
- [`Validator`](#validator)
- [`Crosslink`](#crosslink)
- [`AttestationData`](#attestationdata)
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
- [`IndexedAttestation`](#indexedattestation)
Expand Down Expand Up @@ -82,12 +81,8 @@
- [`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_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)
Expand All @@ -105,7 +100,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)
Expand Down Expand Up @@ -174,6 +168,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) |
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**12` (= 4,096) |
Expand Down Expand Up @@ -215,7 +210,6 @@ The following values are (non-configurable) constants used throughout the specif
| `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
Expand Down Expand Up @@ -305,29 +299,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
# Committee Index
index: uint64
```

#### `AttestationDataAndCustodyBit`
Expand Down Expand Up @@ -507,16 +490,12 @@ class BeaconState(Container):
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
# Shuffling
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -878,61 +857,22 @@ 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`

```python
def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64:
"""
Return the number of committees at ``epoch``.
"""
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
```

#### `get_crosslink_committee`

```python
def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
def get_crosslink_committee(state: BeaconState, slot: Slot, index: uint64) -> Sequence[ValidatorIndex]:
"""
Return the crosslink committee at ``epoch`` for ``shard``.
Return the crosslink committee at ``epoch`` for ``index``.
"""
epoch = compute_epoch_of_slot(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
Expand All @@ -946,18 +886,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
Expand Down Expand Up @@ -1019,7 +947,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.slot, data.index)
return set(index for i, index in enumerate(committee) if bits[i])
```

Expand Down Expand Up @@ -1199,7 +1127,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
Expand Down Expand Up @@ -1230,7 +1157,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)
]
```

Expand All @@ -1248,23 +1175,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
Expand Down Expand Up @@ -1308,20 +1218,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
Expand Down Expand Up @@ -1384,36 +1280,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)
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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])
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
decrease_balance(state, ValidatorIndex(index), penalties1[index])
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
```

#### Registry updates
Expand Down Expand Up @@ -1481,8 +1356,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 = []
Expand Down Expand Up @@ -1609,37 +1482,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
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
committee = get_crosslink_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))
```
Expand Down
2 changes: 1 addition & 1 deletion specs/core/0_fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

# Get state at the `target` to validate attestation and calculate the committees
Expand Down
1 change: 1 addition & 0 deletions specs/core/1_beacon-chain-misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

| Name | Value | Unit | Duration
| - | - | - | - |
| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours |
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
| `MAX_SHARD_RECEIPT_PROOFS` | `2**0` (= 1) | - | - |
| `PERIOD_COMMITTEE_ROOT_LENGTH` | `2**8` (= 256) | periods | ~9 months |
| `MINOR_REWARD_QUOTIENT` | `2**8` (=256) | - | - |
Expand Down
1 change: 1 addition & 0 deletions specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading