commit
53952dd0c9
|
@ -260,15 +260,29 @@ func get_initial_beacon_state*(
|
|||
if get_effective_balance(state, vi) > MAX_DEPOSIT_AMOUNT:
|
||||
activate_validator(state, vi, true)
|
||||
|
||||
# set initial committee shuffling
|
||||
# initial_shuffling + initial_shuffling in spec, but more ugly
|
||||
# TODO remove temporary workaround
|
||||
# previously, shuffling created foo[slot][committee_per_slot]
|
||||
# now that's flattened to [committee_0_slot_0, c_1_s_0, ..., c_2_s_1, c_3_s_1, ...]
|
||||
# so build adapter to keep this working until full conversion to current spec
|
||||
# target structure is array[2 * EPOCH_LENGTH, seq[ShardCommittee]],
|
||||
# where ShardCommittee is: shard*: uint64 / committee*: seq[Uint24]
|
||||
let
|
||||
initial_shuffling =
|
||||
get_shuffling(Eth2Digest(), state.validator_registry, 0, state.slot)
|
||||
get_shuffling(Eth2Digest(), state.validator_registry, state.slot)
|
||||
committee_count_per_slot = initial_shuffling.len div EPOCH_LENGTH
|
||||
|
||||
# initial_shuffling + initial_shuffling in spec, but more ugly
|
||||
for i, n in initial_shuffling:
|
||||
state.shard_committees_at_slots[i] = n
|
||||
state.shard_committees_at_slots[EPOCH_LENGTH + i] = n
|
||||
for i in 0 ..< EPOCH_LENGTH:
|
||||
state.shard_committees_at_slots[i] = @[]
|
||||
state.shard_committees_at_slots[EPOCH_LENGTH + i] = @[]
|
||||
|
||||
for i, committee2 in initial_shuffling:
|
||||
let slot = i div committee_count_per_slot
|
||||
var sc: ShardCommittee
|
||||
sc.shard = i.uint64
|
||||
sc.committee = committee2
|
||||
state.shard_committees_at_slots[slot] = concat(state.shard_committees_at_slots[slot], @[sc])
|
||||
state.shard_committees_at_slots[EPOCH_LENGTH + slot] = concat(state.shard_committees_at_slots[EPOCH_LENGTH + slot], @[sc])
|
||||
|
||||
state
|
||||
|
||||
|
|
|
@ -22,43 +22,38 @@ func min_empty_validator_index*(
|
|||
ZERO_BALANCE_VALIDATOR_TTL.uint64 <= current_slot:
|
||||
return some(i)
|
||||
|
||||
func xorSeed(seed: Eth2Digest, x: uint64): Eth2Digest =
|
||||
## Integers are all encoded as bigendian
|
||||
## Helper for get_shuffling in lieu of generally better bitwise handling
|
||||
## xor least significant/highest-index 8 bytes in place (after copy)
|
||||
result = seed
|
||||
for i in 0 ..< 8:
|
||||
result.data[31 - i] = result.data[31 - i] xor byte((x shr i*8) and 0xff)
|
||||
|
||||
func get_shuffling*(seed: Eth2Digest,
|
||||
validators: openArray[Validator],
|
||||
crosslinking_start_shard: uint64, # TODO remove
|
||||
slot_nonaligned: uint64
|
||||
): array[EPOCH_LENGTH, seq[ShardCommittee]] =
|
||||
## Split up validators into groups at the start of every epoch,
|
||||
## determining at what height they can make attestations and what shard they are making crosslinks for
|
||||
): seq[seq[Uint24]] =
|
||||
## Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``slot``.
|
||||
## Returns a list of ``EPOCH_LENGTH * committees_per_slot`` committees where each
|
||||
## committee is itself a list of validator indices.
|
||||
## Implementation should do the following: http://vitalik.ca/files/ShuffleAndAssign.png
|
||||
|
||||
let
|
||||
slot = slot_nonaligned - slot_nonaligned mod EPOCH_LENGTH
|
||||
|
||||
active_validator_indices = get_active_validator_indices(validators, slot)
|
||||
committees_per_slot = clamp(
|
||||
len(active_validator_indices) div EPOCH_LENGTH div TARGET_COMMITTEE_SIZE,
|
||||
1, SHARD_COUNT div EPOCH_LENGTH).uint64
|
||||
# Shuffle with seed
|
||||
shuffled_active_validator_indices = shuffle(active_validator_indices, seed)
|
||||
# Split the shuffled list into cycle_length pieces
|
||||
validators_per_slot = split(shuffled_active_validator_indices, EPOCH_LENGTH)
|
||||
|
||||
assert validators_per_slot.len() == EPOCH_LENGTH # what split should do..
|
||||
committees_per_slot = get_committee_count_per_slot(len(active_validator_indices)).int
|
||||
|
||||
for slot, slot_indices in validators_per_slot:
|
||||
let
|
||||
shard_indices = split(slot_indices, committees_per_slot)
|
||||
shard_id_start =
|
||||
crosslinking_start_shard + slot.uint64 * committees_per_slot
|
||||
# Shuffle
|
||||
shuffled_active_validator_indices = shuffle(
|
||||
active_validator_indices,
|
||||
xorSeed(seed, slot))
|
||||
|
||||
var committees = newSeq[ShardCommittee](shard_indices.len)
|
||||
for shard_position, indices in shard_indices:
|
||||
committees[shard_position].shard =
|
||||
shard_id_start + shard_position.uint64 mod SHARD_COUNT.uint64
|
||||
committees[shard_position].committee = indices
|
||||
committees[shard_position].total_validator_count =
|
||||
len(active_validator_indices).uint64
|
||||
|
||||
result[slot] = committees
|
||||
# Split the shuffled list into epoch_length * committees_per_slot pieces
|
||||
result = split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH)
|
||||
assert result.len() == committees_per_slot * EPOCH_LENGTH # what split should do..
|
||||
|
||||
func get_new_validator_registry_delta_chain_tip*(
|
||||
current_validator_registry_delta_chain_tip: Eth2Digest,
|
||||
|
@ -75,3 +70,58 @@ func get_new_validator_registry_delta_chain_tip*(
|
|||
slot: slot,
|
||||
flag: flag
|
||||
))
|
||||
|
||||
func get_previous_epoch_committee_count_per_slot(state: BeaconState): uint64 =
|
||||
let previous_active_validators = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
state.previous_epoch_calculation_slot
|
||||
)
|
||||
get_committee_count_per_slot(len(previous_active_validators))
|
||||
|
||||
func get_current_epoch_committee_count_per_slot(state: BeaconState): uint64 =
|
||||
let previous_active_validators = get_active_validator_indices(
|
||||
state.validator_registry,
|
||||
state.current_epoch_calculation_slot
|
||||
)
|
||||
get_committee_count_per_slot(len(previous_active_validators))
|
||||
|
||||
func get_crosslink_committees_at_slot*(state: BeaconState, slot: uint64) : seq[tuple[a: seq[Uint24], b: uint64]] =
|
||||
## Returns the list of ``(committee, shard)`` tuples for the ``slot``.
|
||||
|
||||
let state_epoch_slot = state.slot - (state.slot mod EPOCH_LENGTH)
|
||||
assert state_epoch_slot <= slot + EPOCH_LENGTH
|
||||
assert slot < state_epoch_slot + EPOCH_LENGTH
|
||||
let offset = slot mod EPOCH_LENGTH
|
||||
|
||||
if slot < state_epoch_slot:
|
||||
let
|
||||
committees_per_slot = get_previous_epoch_committee_count_per_slot(state)
|
||||
shuffling = get_shuffling(
|
||||
state.previous_epoch_randao_mix,
|
||||
state.validator_registry,
|
||||
state.previous_epoch_calculation_slot
|
||||
)
|
||||
slot_start_shard = (state.previous_epoch_start_shard + committees_per_slot * offset) mod SHARD_COUNT
|
||||
|
||||
## This duplication is ugly, but keeping for sake of closeness with spec code structure
|
||||
## There are better approaches in general.
|
||||
for i in 0 ..< committees_per_slot.int:
|
||||
result.add (
|
||||
shuffling[(committees_per_slot * offset + i.uint64).int],
|
||||
(slot_start_shard + i.uint64) mod SHARD_COUNT
|
||||
)
|
||||
else:
|
||||
let
|
||||
committees_per_slot = get_current_epoch_committee_count_per_slot(state)
|
||||
shuffling = get_shuffling(
|
||||
state.current_epoch_randao_mix,
|
||||
state.validator_registry,
|
||||
state.current_epoch_calculation_slot
|
||||
)
|
||||
slot_start_shard = (state.current_epoch_start_shard + committees_per_slot * offset) mod SHARD_COUNT
|
||||
|
||||
for i in 0 ..< committees_per_slot.int:
|
||||
result.add (
|
||||
shuffling[(committees_per_slot * offset + i.uint64).int],
|
||||
(slot_start_shard + i.uint64) mod SHARD_COUNT
|
||||
)
|
||||
|
|
|
@ -611,13 +611,15 @@ func processEpoch(state: var BeaconState) =
|
|||
state.finalized_slot = state.justified_slot
|
||||
|
||||
block: # Crosslinks
|
||||
for sac in state.shard_committees_at_slots:
|
||||
for shard_committee in sac:
|
||||
if 3'u64 * total_attesting_balance(shard_committee) >=
|
||||
2'u64 * total_balance_sac(shard_committee):
|
||||
state.latest_crosslinks[shard_committee.shard] = Crosslink(
|
||||
slot: state.slot + EPOCH_LENGTH,
|
||||
shard_block_root: winning_root(shard_committee))
|
||||
for slot in state.slot - 2 * EPOCH_LENGTH ..< state.slot:
|
||||
discard
|
||||
# TODO implement helpers
|
||||
#let crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)
|
||||
#
|
||||
#for crosslink_committee, shard in crosslink_committees_at_slot.items:
|
||||
# if 3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee):
|
||||
# state.latest_crosslinks[shard] = Crosslink(
|
||||
# slot=state.slot, shard_block_root=winning_root(crosslink_committee))
|
||||
|
||||
block: # Justification and finalization
|
||||
let
|
||||
|
@ -705,14 +707,6 @@ func processEpoch(state: var BeaconState) =
|
|||
state.shard_committees_at_slots[i] =
|
||||
state.shard_committees_at_slots[EPOCH_LENGTH + i]
|
||||
|
||||
let next_start_shard =
|
||||
(state.shard_committees_at_slots[^1][^1].shard + 1) mod SHARD_COUNT
|
||||
for i, v in get_shuffling(
|
||||
state.latest_randao_mixes[
|
||||
(state.slot - EPOCH_LENGTH) mod LATEST_RANDAO_MIXES_LENGTH],
|
||||
state.validator_registry, next_start_shard, state.slot):
|
||||
state.shard_committees_at_slots[i + EPOCH_LENGTH] = v
|
||||
|
||||
else:
|
||||
# If a validator registry change does NOT happen
|
||||
for i in 0..<EPOCH_LENGTH:
|
||||
|
@ -725,14 +719,6 @@ func processEpoch(state: var BeaconState) =
|
|||
EPOCH_LENGTH
|
||||
start_shard = state.shard_committees_at_slots[0][0].shard
|
||||
|
||||
if is_power_of_2(epochs_since_last_registry_change):
|
||||
for i, v in get_shuffling(
|
||||
state.latest_randao_mixes[
|
||||
(state.slot - EPOCH_LENGTH) mod LATEST_RANDAO_MIXES_LENGTH],
|
||||
state.validator_registry, start_shard, state.slot):
|
||||
state.shard_committees_at_slots[i + EPOCH_LENGTH] = v
|
||||
# Note that `start_shard` is not changed from the last epoch.
|
||||
|
||||
block: # Final updates
|
||||
state.latest_attestations.keepItIf(
|
||||
not (it.data.slot + EPOCH_LENGTH < state.slot)
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
|
||||
import
|
||||
math,unittest, sequtils,
|
||||
../beacon_chain/spec/[datatypes, digest, validator]
|
||||
../beacon_chain/spec/[helpers, datatypes, digest, validator]
|
||||
|
||||
func sumCommittees(v: openArray[seq[ShardCommittee]]): int =
|
||||
func sumCommittees(v: openArray[seq[Uint24]], reqCommitteeLen: int): int =
|
||||
for x in v:
|
||||
for y in x:
|
||||
inc result, y.committee.len
|
||||
## This only holds when num_validators is divisible by
|
||||
## EPOCH_LENGTH * get_committee_count_per_slot(len(validators))
|
||||
## as, in general, not all committees can be equally sized.
|
||||
assert x.len == reqCommitteeLen
|
||||
inc result, x.len
|
||||
|
||||
suite "Validators":
|
||||
## For now just test that we can compile and execute block processing with mock data.
|
||||
|
@ -19,17 +22,15 @@ suite "Validators":
|
|||
## https://github.com/sigp/lighthouse/blob/ba548e49a52687a655c61b443b6835d79c6d4236/beacon_chain/validator_shuffling/src/shuffle.rs
|
||||
test "Smoke validator shuffling":
|
||||
let
|
||||
num_validators = 32*1024
|
||||
validators = repeat(
|
||||
Validator(
|
||||
exit_slot: FAR_FUTURE_SLOT
|
||||
), 32*1024)
|
||||
|
||||
# TODO the shuffling looks really odd, probably buggy
|
||||
let s = get_shuffling(Eth2Digest(), validators, 0, 0)
|
||||
), num_validators)
|
||||
s = get_shuffling(Eth2Digest(), validators, 0)
|
||||
committees = EPOCH_LENGTH * get_committee_count_per_slot(len(validators)).int
|
||||
check:
|
||||
s.len == EPOCH_LENGTH
|
||||
# 32k validators means 2 shards validated per slot - the aim is to get
|
||||
# TARGET_COMMITTEE_SIZE validators in each shard and there are
|
||||
# EPOCH_LENGTH slots which each will crosslink a different shard
|
||||
s[0].len == 32 * 1024 div (TARGET_COMMITTEE_SIZE * EPOCH_LENGTH)
|
||||
sumCommittees(s) == validators.len() # all validators accounted for
|
||||
s.len == committees
|
||||
# 32k validators: EPOCH_LENGTH slots * committee_count_per_slot =
|
||||
# EPOCH_LENGTH * committee_count_per_slot committees.
|
||||
sumCommittees(s, num_validators div committees) == validators.len() # all validators accounted for
|
||||
|
|
Loading…
Reference in New Issue