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

Replace committee exponential backoff with max progress #808

Merged
merged 10 commits into from
Mar 26, 2019
122 changes: 25 additions & 97 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@
- [`split`](#split)
- [`get_epoch_committee_count`](#get_epoch_committee_count)
- [`get_shuffling`](#get_shuffling)
- [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count)
- [`get_current_epoch_committee_count`](#get_current_epoch_committee_count)
- [`get_next_epoch_committee_count`](#get_next_epoch_committee_count)
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
- [`get_block_root`](#get_block_root)
- [`get_state_root`](#get_state_root)
Expand Down Expand Up @@ -185,8 +183,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) |
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
| `SHUFFLE_ROUND_COUNT` | 90 |
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) |
vbuterin marked this conversation as resolved.
Show resolved Hide resolved

* 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.)
* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // EPOCH_LENGTH`

### Deposit contract

Expand Down Expand Up @@ -598,12 +598,7 @@ The types are defined topologically to aid in facilitating an executable version

# Randomness and committees
'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH],
'previous_shuffling_start_shard': 'uint64',
'current_shuffling_start_shard': 'uint64',
'previous_shuffling_epoch': 'uint64',
'current_shuffling_epoch': 'uint64',
'previous_shuffling_seed': 'bytes32',
'current_shuffling_seed': 'bytes32',

# Finality
'previous_epoch_attestations': [PendingAttestation],
Expand Down Expand Up @@ -826,20 +821,6 @@ def get_shuffling(seed: Bytes32,

**Note**: this definition and the next few definitions make heavy use of repetitive computing. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.

### `get_previous_epoch_committee_count`

```python
def get_previous_epoch_committee_count(state: BeaconState) -> int:
"""
Return the number of committees in the previous epoch of the given ``state``.
"""
previous_active_validators = get_active_validator_indices(
state.validator_registry,
state.previous_shuffling_epoch,
)
return get_epoch_committee_count(len(previous_active_validators))
```

### `get_current_epoch_committee_count`

```python
Expand All @@ -849,25 +830,11 @@ def get_current_epoch_committee_count(state: BeaconState) -> int:
"""
current_active_validators = get_active_validator_indices(
state.validator_registry,
state.current_shuffling_epoch,
get_current_epoch(state),
)
return get_epoch_committee_count(len(current_active_validators))
```

### `get_next_epoch_committee_count`

```python
def get_next_epoch_committee_count(state: BeaconState) -> int:
"""
Return the number of committees in the next epoch of the given ``state``.
"""
next_active_validators = get_active_validator_indices(
state.validator_registry,
get_current_epoch(state) + 1,
)
return get_epoch_committee_count(len(next_active_validators))
```

### `get_crosslink_committees_at_slot`

```python
Expand All @@ -886,40 +853,27 @@ def get_crosslink_committees_at_slot(state: BeaconState,
next_epoch = current_epoch + 1

assert previous_epoch <= epoch <= next_epoch
committees_per_epoch = get_epoch_committee_count(get_active_validator_indices(
state.validator_registry,
epoch,
))

if epoch == current_epoch:
committees_per_epoch = get_current_epoch_committee_count(state)
seed = state.current_shuffling_seed
shuffling_epoch = state.current_shuffling_epoch
shuffling_start_shard = state.current_shuffling_start_shard
elif epoch == previous_epoch:
committees_per_epoch = get_previous_epoch_committee_count(state)
seed = state.previous_shuffling_seed
shuffling_epoch = state.previous_shuffling_epoch
shuffling_start_shard = state.previous_shuffling_start_shard
shuffling_start_shard = (
state.current_shuffling_start_shard - EPOCH_LENGTH * committees_per_epoch
) % SHARD_COUNT
elif epoch == next_epoch:
epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
if registry_change:
committees_per_epoch = get_next_epoch_committee_count(state)
seed = generate_seed(state, next_epoch)
shuffling_epoch = next_epoch
current_committees_per_epoch = get_current_epoch_committee_count(state)
shuffling_start_shard = (state.current_shuffling_start_shard + current_committees_per_epoch) % SHARD_COUNT
elif epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update):
committees_per_epoch = get_next_epoch_committee_count(state)
seed = generate_seed(state, next_epoch)
shuffling_epoch = next_epoch
shuffling_start_shard = state.current_shuffling_start_shard
else:
committees_per_epoch = get_current_epoch_committee_count(state)
seed = state.current_shuffling_seed
shuffling_epoch = state.current_shuffling_epoch
shuffling_start_shard = state.current_shuffling_start_shard
current_epoch_committees = get_current_epoch_committee_count(state)
shuffling_start_shard = (
state.current_shuffling_start_shard + EPOCH_LENGTH * current_epoch_committees
) % SHARD_COUNT

shuffling = get_shuffling(
seed,
generate_seed(state, epoch),
state.validator_registry,
shuffling_epoch,
epoch,
)
offset = slot % SLOTS_PER_EPOCH
committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH
Expand Down Expand Up @@ -1529,12 +1483,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],

# Randomness and committees
latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]),
previous_shuffling_start_shard=GENESIS_START_SHARD,
current_shuffling_start_shard=GENESIS_START_SHARD,
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
previous_shuffling_epoch=GENESIS_EPOCH,
current_shuffling_epoch=GENESIS_EPOCH,
previous_shuffling_seed=ZERO_HASH,
current_shuffling_seed=ZERO_HASH,

# Finality
previous_epoch_attestations=[],
Expand Down Expand Up @@ -1574,7 +1523,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH))
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
state.latest_active_index_roots[index] = genesis_active_index_root
state.current_shuffling_seed = generate_seed(state, GENESIS_EPOCH)

return state
```
Expand Down Expand Up @@ -1855,7 +1803,7 @@ def process_crosslinks(state: BeaconState) -> None:
total_balance = get_total_balance(state, crosslink_committee)
if 3 * participating_balance >= 2 * total_balance:
state.latest_crosslinks[shard] = Crosslink(
epoch=slot_to_epoch(slot),
epoch=min(slot_to_epoch(slot), state.latest_crosslinks[shard].epoch + MAX_CROSSLINK_EPOCHS),
crosslink_data_root=winning_root
)
```
Expand Down Expand Up @@ -2060,14 +2008,6 @@ def should_update_validator_registry(state: BeaconState) -> bool:
# Must have finalized a new block
if state.finalized_epoch <= state.validator_registry_update_epoch:
return False
# Must have processed new crosslinks on all shards of the current epoch
shards_to_check = [
(state.current_shuffling_start_shard + i) % SHARD_COUNT
for i in range(get_current_epoch_committee_count(state))
]
for shard in shards_to_check:
if state.latest_crosslinks[shard].epoch <= state.validator_registry_update_epoch:
return False
return True
```

Expand Down Expand Up @@ -2119,30 +2059,17 @@ def update_validator_registry(state: BeaconState) -> None:
Run the following function:

```python
def update_registry_and_shuffling_data(state: BeaconState) -> None:
# First set previous shuffling data to current shuffling data
state.previous_shuffling_epoch = state.current_shuffling_epoch
state.previous_shuffling_start_shard = state.current_shuffling_start_shard
state.previous_shuffling_seed = state.current_shuffling_seed
def update_registry(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
next_epoch = current_epoch + 1
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
# Check if we should update, and if so, update
if should_update_validator_registry(state):
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
update_validator_registry(state)
# If we update the registry, update the shuffling data and shards as well
state.current_shuffling_epoch = next_epoch
state.current_shuffling_start_shard = (
state.current_shuffling_start_shard +
get_current_epoch_committee_count(state) % SHARD_COUNT
)
state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)
else:
# If processing at least one crosslink keeps failing, then reshuffle every power of two,
# but don't update the current_shuffling_start_shard
epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
if epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update):
state.current_shuffling_epoch = next_epoch
state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)
# If we update the registry, update the shuffling data 2/3 or and shards as well
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
state.current_shuffling_start_shard = (
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
state.current_shuffling_start_shard +
get_current_epoch_committee_count(state) % SHARD_COUNT
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
)
```

**Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch.
Expand Down Expand Up @@ -2397,7 +2324,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
# the attestation is trying to create
Crosslink(
crosslink_data_root=attestation.data.crosslink_data_root,
epoch=slot_to_epoch(attestation.data.slot)
epoch=min(slot_to_epoch(attestation.data.slot),
JustinDrake marked this conversation as resolved.
Show resolved Hide resolved
attestation.data.previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS)
)
}
assert state.latest_crosslinks[attestation.data.shard] in acceptable_crosslink_data
Expand Down