From b4b60ae16ac6a8278d2a1ebca6d8e59c7f696b55 Mon Sep 17 00:00:00 2001 From: Vitalik Buterin Date: Thu, 18 Oct 2018 13:54:57 -0400 Subject: [PATCH 01/10] Assign validators to persistent committees for shard block production --- specs/beacon-chain.md | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index c22b71a24..111966c9b 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -42,6 +42,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours | | `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 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) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — | @@ -205,6 +206,9 @@ The `CrystallizedState` has the following fields: 'justified_streak': 'uint64', # Committee members and their assigned shard, per slot 'shard_and_committee_for_slots': [[ShardAndCommittee]], + # Persistent shard committees + 'current_persistent_committees': [['uint24']], + 'next_persistent_committees': [['uint24']], # Total deposits penalized in the given withdrawal period 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) @@ -442,6 +446,15 @@ Here's a diagram of what's going on: ![](http://vitalik.ca/files/ShuffleAndAssign.png?1) +We also make a function for generating persistent committees: + +```python +def get_persistent_shuffling(validators: List[ValidatorRecord], seed: Hash32): + active_validators = get_active_validator_indices(validators) + shuffled_active_validator_indices = shuffle(active_validators, seed) + return split(shuffled_active_validator_indices, SHARD_COUNT) +``` + We also define two functions for retrieving data from the state: ```python @@ -716,15 +729,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 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_ONLINE_DEPOSIT_SIZE` 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 A validator set change can happen after a state recalculation if all of the following criteria are satisfied: @@ -791,13 +795,32 @@ def change_validators(validators: List[ValidatorRecord]) -> None: # STUB: withdraw to shard chain ``` -Finally: +Then: * 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` * 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)` +#### Finally... + +* 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 `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:]` + +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 `current_persistent_committees` and `next_persistent_committees` objects. +* If the validator was added with index `validator_index`, let `assigned_shard = hash(crystallized_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`. Add `validator_index` to the end of `next_persistent_committees[assigned_shard]`. + +If `block.slot % SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD == 0`, then: + +* Set `current_persistent_committees = next_persistent_committees` +* Set `next_persistent_committees = get_persistent_shuffling(validators, crystallized_state.randao_mix)` + ### TODO Note: This spec is ~60% complete. From 3e57bd526615ea801726b6651f5dbd25312dd9f9 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 18 Oct 2018 15:47:54 -0400 Subject: [PATCH 02/10] Update beacon-chain.md --- specs/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 111966c9b..bcf45c68c 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -449,7 +449,7 @@ Here's a diagram of what's going on: We also make a function for generating persistent committees: ```python -def get_persistent_shuffling(validators: List[ValidatorRecord], seed: Hash32): +def get_persistent_shuffling(validators: List[ValidatorRecord], seed: Hash32) -> List[List[int]]: active_validators = get_active_validator_indices(validators) shuffled_active_validator_indices = shuffle(active_validators, seed) return split(shuffled_active_validator_indices, SHARD_COUNT) From 3e7adb3981bfab6c726fa98bd66c5b75f30407e6 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 19 Oct 2018 12:05:00 -0400 Subject: [PATCH 03/10] Moved randao_mix from crystallized to active state --- specs/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index bcf45c68c..fd2b47790 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -814,12 +814,12 @@ Then: 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 `current_persistent_committees` and `next_persistent_committees` objects. -* If the validator was added with index `validator_index`, let `assigned_shard = hash(crystallized_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`. Add `validator_index` to the end of `next_persistent_committees[assigned_shard]`. +* If the validator was added with index `validator_index`, let `assigned_shard = hash(active_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`. Add `validator_index` to the end of `next_persistent_committees[assigned_shard]`. If `block.slot % SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD == 0`, then: * Set `current_persistent_committees = next_persistent_committees` -* Set `next_persistent_committees = get_persistent_shuffling(validators, crystallized_state.randao_mix)` +* Set `next_persistent_committees = get_persistent_shuffling(validators, active_state.randao_mix)` ### TODO From 8f3318813fa572a2afd6e84151c6c00fd42a2dd8 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 19 Oct 2018 12:06:25 -0400 Subject: [PATCH 04/10] Update beacon-chain.md --- specs/beacon-chain.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index fd2b47790..791d3f21d 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -795,21 +795,18 @@ def change_validators(validators: List[ValidatorRecord]) -> None: # STUB: withdraw to shard chain ``` -Then: +#### Finally... * 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` -* 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)` - -#### Finally... - * 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 `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:]` +* 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)` For any validator that was added or removed from the active validator list during this state recalculation: From 5d36ef4799e6d0a716bfb336390b4c5c88275fa9 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 19 Oct 2018 16:01:15 -0400 Subject: [PATCH 05/10] Update beacon-chain.md --- specs/beacon-chain.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 791d3f21d..6c4011d3b 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -818,6 +818,23 @@ If `block.slot % SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD == 0`, then: * Set `current_persistent_committees = next_persistent_committees` * Set `next_persistent_committees = get_persistent_shuffling(validators, active_state.randao_mix)` +``` +### Possible alternative (run every state recalculation): + +active_validator_indices = get_active_validator_indices(validators) +for i in range(len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD): + 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 + crystallized_state.persistent_shard_reassignments.append(ShardReassignmentRecord(validator_id=vid, shard=new_shard, slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD)) + +while len(crystallized_state.persistent_shard_reassignments) > 0 and crystallized_state.persistent_shard_reassignments[0].slot <= block.slot: + rec = crystallized_state.persistent_shard_reassignments[0] + for c in crystallized_state.current_persistent_committees: + if rec.validator_id in c: + c.pop(c.index(rec.validator_id)) + crystallized_state.current_persistent_committees[rec.shard].append(vid) +``` + ### TODO Note: This spec is ~60% complete. From 1276f1334be098c4d4414b35928ced3a46b5257d Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 30 Oct 2018 04:45:28 -0400 Subject: [PATCH 06/10] Update beacon-chain.md --- specs/beacon-chain.md | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 6c4011d3b..2e1b74eca 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -207,8 +207,8 @@ The `CrystallizedState` has the following fields: # Committee members and their assigned shard, per slot 'shard_and_committee_for_slots': [[ShardAndCommittee]], # Persistent shard committees - 'current_persistent_committees': [['uint24']], - 'next_persistent_committees': [['uint24']], + 'persistent_committees': [['uint24']], + 'shard_reassignment_records': [ShardReassignmentRecord], # Total deposits penalized in the given withdrawal period 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) @@ -268,6 +268,19 @@ A `ShardAndCommittee` object has the following fields: } ``` +A `ShardReassignmentRecord` object has the following fields: + +```python +{ + # Which validator to reassign + 'validator_id': 'uint64', + # To which shard + 'shard': 'uint16', + # When + 'slot': 'uint64' +} +``` + ## Beacon chain processing The beacon chain is the "main chain" of the PoS system. The beacon chain's main responsibilities are: @@ -446,15 +459,6 @@ Here's a diagram of what's going on: ![](http://vitalik.ca/files/ShuffleAndAssign.png?1) -We also make a function for generating persistent committees: - -```python -def get_persistent_shuffling(validators: List[ValidatorRecord], seed: Hash32) -> List[List[int]]: - active_validators = get_active_validator_indices(validators) - shuffled_active_validator_indices = shuffle(active_validators, seed) - return split(shuffled_active_validator_indices, SHARD_COUNT) -``` - We also define two functions for retrieving data from the state: ```python @@ -486,6 +490,8 @@ def add_validator_set_change_record(crystallized_state: CrystallizedState, bytes1(flag) + bytes3(index) + bytes32(pubkey)) ``` +We define another set of helpers: `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. + Finally, we abstractly define `int_sqrt(n)` for use in reward/penalty calculations as the largest integer `k` such that `k**2 <= n`. Here is one possible implementation, though clients are free to use their own including standard libraries for [integer square root](https://en.wikipedia.org/wiki/Integer_square_root) if available and meet the specification. ```python @@ -537,6 +543,8 @@ def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState, last_justified_slot=0, justified_streak=0, shard_and_committee_for_slots=x + x, + persistent_shuffling=split(shuffle(validators, b'x\00'*32), SHARD_COUNT), + shard_reassignment_records=[], deposits_penalized_in_period=[], validator_set_delta_hash_chain=bytes([0] * 32), # stub pre_fork_version=INITIAL_FORK_VERSION, @@ -810,17 +818,12 @@ def change_validators(validators: List[ValidatorRecord]) -> None: 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 `current_persistent_committees` and `next_persistent_committees` objects. +* If the validator was removed, remove their index from the `persistent_committees` and `next_persistent_committees` objects. * If the validator was added with index `validator_index`, let `assigned_shard = hash(active_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`. Add `validator_index` to the end of `next_persistent_committees[assigned_shard]`. -If `block.slot % SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD == 0`, then: - -* Set `current_persistent_committees = next_persistent_committees` -* Set `next_persistent_committees = get_persistent_shuffling(validators, active_state.randao_mix)` - -``` -### Possible alternative (run every state recalculation): +Now run the following code to reshuffle a few proposers: +```python active_validator_indices = get_active_validator_indices(validators) for i in range(len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD): vid = active_validator_indices[hash(active_state.randao_mix + bytes8(i * 2)) % len(active_validator_indices)] @@ -829,10 +832,10 @@ for i in range(len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANG while len(crystallized_state.persistent_shard_reassignments) > 0 and crystallized_state.persistent_shard_reassignments[0].slot <= block.slot: rec = crystallized_state.persistent_shard_reassignments[0] - for c in crystallized_state.current_persistent_committees: + for c in crystallized_state.persistent_committees: if rec.validator_id in c: c.pop(c.index(rec.validator_id)) - crystallized_state.current_persistent_committees[rec.shard].append(vid) + crystallized_state.persistent_committees[rec.shard].append(vid) ``` ### TODO From 0df4141f051ba3401771ac2bc685ea5aa7fac20c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 1 Nov 2018 08:08:23 +0100 Subject: [PATCH 07/10] make bytes more uniform --- specs/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 2e1b74eca..187e8e5f7 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -543,7 +543,7 @@ def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState, last_justified_slot=0, justified_streak=0, shard_and_committee_for_slots=x + x, - persistent_shuffling=split(shuffle(validators, b'x\00'*32), SHARD_COUNT), + persistent_shuffling=split(shuffle(validators, bytes([0] * 32)), SHARD_COUNT), shard_reassignment_records=[], deposits_penalized_in_period=[], validator_set_delta_hash_chain=bytes([0] * 32), # stub From cc910d831a680c2247872ec7921d87bdaa300ff8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 5 Nov 2018 13:38:25 +0100 Subject: [PATCH 08/10] cleanup shard reassignment logic --- specs/beacon-chain.md | 55 ++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 187e8e5f7..bddf2ab57 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -208,7 +208,7 @@ The `CrystallizedState` has the following fields: 'shard_and_committee_for_slots': [[ShardAndCommittee]], # Persistent shard committees 'persistent_committees': [['uint24']], - 'shard_reassignment_records': [ShardReassignmentRecord], + 'persistent_shard_reassignments': [ShardReassignmentRecord], # Total deposits penalized in the given withdrawal period 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) @@ -273,7 +273,7 @@ A `ShardReassignmentRecord` object has the following fields: ```python { # Which validator to reassign - 'validator_id': 'uint64', + 'validator_index': 'uint64', # To which shard 'shard': 'uint16', # When @@ -478,6 +478,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. +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: ```python @@ -490,8 +492,6 @@ def add_validator_set_change_record(crystallized_state: CrystallizedState, bytes1(flag) + bytes3(index) + bytes32(pubkey)) ``` -We define another set of helpers: `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. - Finally, we abstractly define `int_sqrt(n)` for use in reward/penalty calculations as the largest integer `k` such that `k**2 <= n`. Here is one possible implementation, though clients are free to use their own including standard libraries for [integer square root](https://en.wikipedia.org/wiki/Integer_square_root) if available and meet the specification. ```python @@ -803,38 +803,51 @@ def change_validators(validators: List[ValidatorRecord]) -> None: # STUB: withdraw to shard chain ``` -#### Finally... - -* 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 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 `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:]` * 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)` +#### Finally... + +* Remove all attestation records older than slot `crystallized_state.last_state_recalculation_slot` +* Set `crystallized_state.validator_set_change_slot = crystallized_state.last_state_recalculation_slot` +* 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 `crystallized_state.last_state_recalculation_slot += CYCLE_LENGTH` +* Empty the `active_state.pending_specials` list +* 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:]` + 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 `next_persistent_committees` objects. -* If the validator was added with index `validator_index`, let `assigned_shard = hash(active_state.randao_mix + bytes8(validator_index)) % SHARD_COUNT`. Add `validator_index` to the end of `next_persistent_committees[assigned_shard]`. +* If the validator was removed, remove their index from the `persistent_committees` and remove any `ShardReassignmentRecord`s containing their index from `persistent_shard_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_shard_reassignments` Now run the following code to reshuffle a few proposers: ```python active_validator_indices = get_active_validator_indices(validators) -for i in range(len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD): +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 - crystallized_state.persistent_shard_reassignments.append(ShardReassignmentRecord(validator_id=vid, shard=new_shard, slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD)) - + shard_reassignment_record = ShardReassignmentRecord( + validator_index=vid, + shard=new_shard, + slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD + ) + crystallized_state.persistent_shard_reassignments.append(shard_reassignment_record) + while len(crystallized_state.persistent_shard_reassignments) > 0 and crystallized_state.persistent_shard_reassignments[0].slot <= block.slot: - rec = crystallized_state.persistent_shard_reassignments[0] - for c in crystallized_state.persistent_committees: - if rec.validator_id in c: - c.pop(c.index(rec.validator_id)) + rec = crystallized_state.persistent_shard_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) ``` From 236ac268673b609a2560cd4bfc9bcfce8eab9631 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 5 Nov 2018 13:41:03 +0100 Subject: [PATCH 09/10] rename var --- specs/beacon-chain.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index bddf2ab57..88245f159 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -208,7 +208,7 @@ The `CrystallizedState` has the following fields: 'shard_and_committee_for_slots': [[ShardAndCommittee]], # Persistent shard committees 'persistent_committees': [['uint24']], - 'persistent_shard_reassignments': [ShardReassignmentRecord], + 'persistent_committee_reassignments': [ShardReassignmentRecord], # Total deposits penalized in the given withdrawal period 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) @@ -820,11 +820,11 @@ def change_validators(validators: List[ValidatorRecord]) -> None: 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_shard_reassignments`. +* 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_shard_reassignments` + * Append `reassignment_record` to the end of `persistent_committee_reassignments` Now run the following code to reshuffle a few proposers: @@ -839,10 +839,10 @@ for i in range(num_validators_to_reshuffle): shard=new_shard, slot=block.slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD ) - crystallized_state.persistent_shard_reassignments.append(shard_reassignment_record) + crystallized_state.persistent_committee_reassignments.append(shard_reassignment_record) -while len(crystallized_state.persistent_shard_reassignments) > 0 and crystallized_state.persistent_shard_reassignments[0].slot <= block.slot: - rec = crystallized_state.persistent_shard_reassignments.pop(0) +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( From 189d44f5a5e06608953ef4c0d41dd55b2f263623 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 5 Nov 2018 13:45:08 +0100 Subject: [PATCH 10/10] move validator_set_last_changed to the validator set change section --- specs/beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 88245f159..a53771a04 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -803,6 +803,7 @@ def change_validators(validators: List[ValidatorRecord]) -> None: # STUB: withdraw to shard chain ``` +* 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` * 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` @@ -811,12 +812,11 @@ def change_validators(validators: List[ValidatorRecord]) -> None: #### Finally... * Remove all attestation records older than slot `crystallized_state.last_state_recalculation_slot` -* Set `crystallized_state.validator_set_change_slot = crystallized_state.last_state_recalculation_slot` -* 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 `crystallized_state.last_state_recalculation_slot += CYCLE_LENGTH` * 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: