Merge pull request #89 from ethereum/vitalik7

Assign validators to persistent committees for shard block production
This commit is contained in:
Danny Ryan 2018-11-05 13:49:19 +01:00 committed by GitHub
commit ba25653da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 64 additions and 11 deletions

View File

@ -43,6 +43,7 @@ The primary source of load on the beacon chain are "attestations". Attestations
| `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours | | `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours |
| `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 days | | `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 days |
| `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days | | `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days |
| `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | 2**16 (= 65,536) | slots | ~12 days |
| `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — | | `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — |
| `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — |
| `LOGOUT_MESSAGE` | `"LOGOUT"` | — | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — |
@ -206,6 +207,9 @@ The `CrystallizedState` has the following fields:
'justified_streak': 'uint64', 'justified_streak': 'uint64',
# Committee members and their assigned shard, per slot # Committee members and their assigned shard, per slot
'shard_and_committee_for_slots': [[ShardAndCommittee]], 'shard_and_committee_for_slots': [[ShardAndCommittee]],
# Persistent shard committees
'persistent_committees': [['uint24']],
'persistent_committee_reassignments': [ShardReassignmentRecord],
# Total deposits penalized in the given withdrawal period # Total deposits penalized in the given withdrawal period
'deposits_penalized_in_period': ['uint32'], 'deposits_penalized_in_period': ['uint32'],
# Hash chain of validator set changes (for light clients to easily track deltas) # Hash chain of validator set changes (for light clients to easily track deltas)
@ -265,6 +269,19 @@ A `ShardAndCommittee` object has the following fields:
} }
``` ```
A `ShardReassignmentRecord` object has the following fields:
```python
{
# Which validator to reassign
'validator_index': 'uint64',
# To which shard
'shard': 'uint16',
# When
'slot': 'uint64'
}
```
## Beacon chain processing ## Beacon chain processing
The beacon chain is the "main chain" of the PoS system. The beacon chain's main responsibilities are: The beacon chain is the "main chain" of the PoS system. The beacon chain's main responsibilities are:
@ -459,6 +476,8 @@ def get_block_hash(active_state: ActiveState,
`get_block_hash(_, _, s)` should always return the block in the beacon chain at slot `s`, and `get_shards_and_committees_for_slot(_, s)` should not change unless the validator set changes. `get_block_hash(_, _, s)` should always return the block in the beacon chain at slot `s`, and `get_shards_and_committees_for_slot(_, s)` should not change unless the validator set changes.
We define another set of helpers to be used throughout: `bytes1(x): return x.to_bytes(1, 'big')`, `bytes2(x): return x.to_bytes(2, 'big')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32.
We define a function to "add a link" to the validator hash chain, used when a validator is added or removed: We define a function to "add a link" to the validator hash chain, used when a validator is added or removed:
```python ```python
@ -522,6 +541,8 @@ def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState,
last_justified_slot=0, last_justified_slot=0,
justified_streak=0, justified_streak=0,
shard_and_committee_for_slots=x + x, shard_and_committee_for_slots=x + x,
persistent_shuffling=split(shuffle(validators, bytes([0] * 32)), SHARD_COUNT),
shard_reassignment_records=[],
deposits_penalized_in_period=[], deposits_penalized_in_period=[],
validator_set_delta_hash_chain=bytes([0] * 32), # stub validator_set_delta_hash_chain=bytes([0] * 32), # stub
pre_fork_version=INITIAL_FORK_VERSION, pre_fork_version=INITIAL_FORK_VERSION,
@ -727,15 +748,6 @@ For each `SpecialRecord` `obj` in `active_state.pending_specials`:
* **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, crystallized_state, penalize=True, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, crystallized_state, penalize=True, current_slot=block.slot)`
* **[covers RANDAO updates]**: If `obj.kind == RANDAO_REVEAL`, interpret `data[0]` as an integer and `data[1]` as a hash32. Set `validators[data[0]].randao_commitment = data[1]`. * **[covers RANDAO updates]**: If `obj.kind == RANDAO_REVEAL`, interpret `data[0]` as an integer and `data[1]` as a hash32. Set `validators[data[0]].randao_commitment = data[1]`.
#### Finally...
* For any validator with index `v` with balance less than `MIN_BALANCE` and status `ACTIVE`, run `exit_validator(v, crystallized_state, penalize=False, current_slot=block.slot)`
* Set `crystallized_state.last_state_recalculation_slot += CYCLE_LENGTH`
* Remove all attestation records older than slot `crystallized_state.last_state_recalculation_slot`
* Empty the `active_state.pending_specials` list
* Set `active_state.recent_block_hashes = active_state.recent_block_hashes[CYCLE_LENGTH:]`
* Set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]`
### Validator set change ### Validator set change
A validator set change can happen after a state recalculation if all of the following criteria are satisfied: A validator set change can happen after a state recalculation if all of the following criteria are satisfied:
@ -802,13 +814,54 @@ def change_validators(validators: List[ValidatorRecord]) -> None:
# STUB: withdraw to shard chain # STUB: withdraw to shard chain
``` ```
Finally:
* Set `crystallized_state.validator_set_change_slot = crystallized_state.last_state_recalculation_slot` * Set `crystallized_state.validator_set_change_slot = crystallized_state.last_state_recalculation_slot`
* For all `c` in `crystallized_state.crosslinks`, set `c.recently_changed = False` * For all `c` in `crystallized_state.crosslinks`, set `c.recently_changed = False`
* Set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]`
* Let `next_start_shard = (shard_and_committee_for_slots[-1][-1].shard + 1) % SHARD_COUNT` * Let `next_start_shard = (shard_and_committee_for_slots[-1][-1].shard + 1) % SHARD_COUNT`
* Set `shard_and_committee_for_slots[CYCLE_LENGTH:] = get_new_shuffling(active_state.randao_mix, validators, next_start_shard)` * Set `shard_and_committee_for_slots[CYCLE_LENGTH:] = get_new_shuffling(active_state.randao_mix, validators, next_start_shard)`
#### Finally...
* Remove all attestation records older than slot `crystallized_state.last_state_recalculation_slot`
* Empty the `active_state.pending_specials` list
* For any validator with index `v` with balance less than `MIN_ONLINE_DEPOSIT_SIZE` and status `ACTIVE`, run `exit_validator(v, crystallized_state, penalize=False, current_slot=block.slot)`
* Set `active_state.recent_block_hashes = active_state.recent_block_hashes[CYCLE_LENGTH:]`
* If a validator set change did _not_ occur during this state recalculation, set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]`
* Set `crystallized_state.last_state_recalculation_slot += CYCLE_LENGTH`
For any validator that was added or removed from the active validator list during this state recalculation:
* If the validator was removed, remove their index from the `persistent_committees` and remove any `ShardReassignmentRecord`s containing their index from `persistent_committee_reassignments`.
* If the validator was added with index `validator_index`:
* let `assigned_shard = hash(active_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`
* let `reassignment_record = ShardReassignmentRecord(validator_index=validator_index, shard=assigned_shard, slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD)`
* Append `reassignment_record` to the end of `persistent_committee_reassignments`
Now run the following code to reshuffle a few proposers:
```python
active_validator_indices = get_active_validator_indices(validators)
num_validators_to_reshuffle = len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD
for i in range(num_validators_to_reshuffle):
vid = active_validator_indices[hash(active_state.randao_mix + bytes8(i * 2)) % len(active_validator_indices)]
new_shard = hash(active_state.randao_mix + bytes8(i * 2 + 1)) % SHARD_COUNT
shard_reassignment_record = ShardReassignmentRecord(
validator_index=vid,
shard=new_shard,
slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD
)
crystallized_state.persistent_committee_reassignments.append(shard_reassignment_record)
while len(crystallized_state.persistent_committee_reassignments) > 0 and crystallized_state.persistent_committee_reassignments[0].slot <= block.slot:
rec = crystallized_state.persistent_committee_reassignments.pop(0)
for committee in crystallized_state.persistent_committees:
if rec.validator_index in committee:
committee.pop(
committee.index(rec.validator_index)
)
crystallized_state.persistent_committees[rec.shard].append(vid)
```
### TODO ### TODO
Note: This spec is ~65% complete. Note: This spec is ~65% complete.