epochs replace slots as main first-class entity; several mechanical renamings (ForkData -> Fork; .fork_data -> .fork; remove stub custody types/code; some type aliasing to keep slots and epochs more enforceably separate; fix test_state_transition.nim assumption that GENESIS_SLOT == 0

This commit is contained in:
Dustin Brody 2019-01-28 19:22:22 -08:00
parent 7a7903535c
commit 4574178836
11 changed files with 281 additions and 246 deletions

View File

@ -89,13 +89,13 @@ proc sync*(node: BeaconNode): Future[bool] {.async.} =
node.beaconState = await obtainTrustedStateSnapshot(node.db)
else:
node.beaconState = persistedState[]
var targetSlot = toSlot timeSinceGenesis(node.beaconState)
var targetSlot = (toSlot timeSinceGenesis(node.beaconState))
let t = now()
if t < node.beaconState.genesisTime * 1000:
await sleepAsync int(node.beaconState.genesisTime * 1000 - t)
while node.beaconState.finalized_slot < targetSlot:
while node.beaconState.finalized_epoch < targetSlot.slot_to_epoch:
var (peer, changeLog) = await node.network.getValidatorChangeLog(
node.beaconState.validator_registry_delta_chain_tip)
@ -152,10 +152,10 @@ proc makeAttestation(node: BeaconNode,
doAssert node != nil
doAssert validator != nil
if node.beaconState.slot == node.beaconState.justified_slot:
if get_current_epoch(node.beaconState) == node.beaconState.justified_epoch:
return
let justifiedBlockRoot = get_block_root(node.beaconState, node.beaconState.justified_slot)
let justifiedBlockRoot = get_block_root(node.beaconState, get_epoch_start_slot(node.beaconState.justified_epoch))
var attestationData = AttestationData(
slot: slot,
@ -164,7 +164,7 @@ proc makeAttestation(node: BeaconNode,
epoch_boundary_root: Eth2Digest(), # TODO
shard_block_root: Eth2Digest(), # TODO
latest_crosslink_root: Eth2Digest(), # TODO
justified_slot: node.beaconState.justified_slot,
justified_epoch: node.beaconState.justified_epoch,
justified_block_root: justifiedBlockRoot)
let validatorSignature = await validator.signAttestation(attestationData)

View File

@ -39,7 +39,7 @@ func validate_proof_of_possession(state: BeaconState,
hash_tree_root_final(proof_of_possession_data).data,
proof_of_possession,
get_domain(
state.fork_data,
state.fork,
state.slot,
DOMAIN_DEPOSIT,
)
@ -70,10 +70,10 @@ func process_deposit(state: var BeaconState,
withdrawal_credentials: withdrawal_credentials,
randao_commitment: randao_commitment,
randao_layers: 0,
activation_slot: FAR_FUTURE_SLOT,
exit_slot: FAR_FUTURE_SLOT,
withdrawal_slot: FAR_FUTURE_SLOT,
penalized_slot: FAR_FUTURE_SLOT,
activation_epoch: FAR_FUTURE_EPOCH,
exit_epoch: FAR_FUTURE_EPOCH,
withdrawal_epoch: FAR_FUTURE_EPOCH,
penalized_epoch: FAR_FUTURE_EPOCH,
exit_count: 0,
status_flags: 0,
custody_commitment: custody_commitment,
@ -101,21 +101,19 @@ func process_deposit(state: var BeaconState,
state.validator_balances[index] += deposit
index.Uint24
func get_entry_exit_effect_epoch*(epoch: EpochNumber): EpochNumber =
## An entry or exit triggered in the ``epoch`` given by the input takes effect at
## the epoch given by the output.
epoch + 1 + ENTRY_EXIT_DELAY
# TODO: Uint24 -> ValidatorIndex
func activate_validator(state: var BeaconState,
index: Uint24,
genesis: bool) =
## Activate the validator with the given ``index``.
let validator = addr state.validator_registry[index]
validator.activation_slot = if genesis: GENESIS_SLOT else: state.slot + ENTRY_EXIT_DELAY
state.validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
state.validator_registry_delta_chain_tip,
index,
validator.pubkey,
validator.activation_slot,
ACTIVATION,
)
validator.activation_epoch = if genesis: GENESIS_EPOCH else: get_entry_exit_effect_epoch(get_current_epoch(state))
func initiate_validator_exit(state: var BeaconState,
index: Uint24) =
@ -131,44 +129,29 @@ func exit_validator*(state: var BeaconState,
let validator = addr state.validator_registry[index]
# The following updates only occur if not previous exited
if validator.exit_slot <= state.slot + ENTRY_EXIT_DELAY:
if validator.exit_epoch <= get_entry_exit_effect_epoch(get_current_epoch(state)):
return
validator.exit_slot = state.slot + ENTRY_EXIT_DELAY
validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state))
# The following updates only occur if not previous exited
state.validator_registry_exit_count += 1
validator.exit_count = state.validator_registry_exit_count
state.validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
state.validator_registry_delta_chain_tip,
index,
validator.pubkey,
validator.exit_slot,
ValidatorSetDeltaFlags.EXIT
)
func process_penalties_and_exits_eligible(state: BeaconState, index: int): bool =
let validator = state.validator_registry[index]
if validator.penalized_slot <= state.slot:
# strangely uppercase variable-ish name
let PENALIZED_WITHDRAWAL_TIME = (LATEST_PENALIZED_EXIT_LENGTH * EPOCH_LENGTH div 2).uint64
return state.slot >= validator.penalized_slot + PENALIZED_WITHDRAWAL_TIME
else:
return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME
func process_penalties_and_exits(state: var BeaconState) =
# The active validators
let active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)
let
current_epoch = get_current_epoch(state)
# The active validators
active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)
# The total effective balance of active validators
var total_balance : uint64 = 0
for i in active_validator_indices:
total_balance += get_effective_balance(state, i)
for index, validator in state.validator_registry:
if (state.slot div EPOCH_LENGTH) == (validator.penalized_slot div EPOCH_LENGTH) + LATEST_PENALIZED_EXIT_LENGTH div 2:
if current_epoch == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH div 2:
let
e = ((state.slot div EPOCH_LENGTH) mod LATEST_PENALIZED_EXIT_LENGTH).int
e = (current_epoch mod LATEST_PENALIZED_EXIT_LENGTH).int
total_at_start = state.latest_penalized_exit_balances[(e + 1) mod LATEST_PENALIZED_EXIT_LENGTH]
total_at_end = state.latest_penalized_exit_balances[e]
total_penalties = total_at_end - total_at_start
@ -211,29 +194,31 @@ func get_initial_beacon_state*(
# Misc
slot: GENESIS_SLOT,
genesis_time: genesis_time,
fork_data: ForkData(
# TODO pre_fork_version -> previous_version, post_fork_version -> current_version,
# rm fork_slot init in favor of epoch
fork: Fork(
pre_fork_version: GENESIS_FORK_VERSION,
post_fork_version: GENESIS_FORK_VERSION,
fork_slot: GENESIS_SLOT,
),
validator_registry_update_slot: GENESIS_SLOT,
validator_registry_update_epoch: GENESIS_EPOCH,
validator_registry_exit_count: 0,
validator_registry_delta_chain_tip: ZERO_HASH,
# Randomness and committees
previous_epoch_start_shard: GENESIS_START_SHARD,
current_epoch_start_shard: GENESIS_START_SHARD,
previous_epoch_calculation_slot: GENESIS_SLOT,
current_epoch_calculation_slot: GENESIS_SLOT,
previous_epoch_randao_mix: ZERO_HASH,
current_epoch_randao_mix: ZERO_HASH,
previous_calculation_epoch: GENESIS_EPOCH,
current_calculation_epoch: GENESIS_EPOCH,
previous_epoch_seed: ZERO_HASH,
current_epoch_seed: ZERO_HASH,
# Finality
previous_justified_slot: GENESIS_SLOT,
justified_slot: GENESIS_SLOT,
previous_justified_epoch: GENESIS_EPOCH,
justified_epoch: GENESIS_EPOCH,
justification_bitfield: 0,
finalized_slot: GENESIS_SLOT,
finalized_epoch: GENESIS_EPOCH,
# Deposit root
latest_eth1_data: latest_eth1_data,
@ -268,13 +253,6 @@ func get_block_root*(state: BeaconState,
doAssert slot < state.slot
state.latest_block_roots[slot mod LATEST_BLOCK_ROOTS_LENGTH]
func get_randao_mix*(state: BeaconState,
slot: uint64): Eth2Digest =
## Returns the randao mix at a recent ``slot``.
assert state.slot < slot + LATEST_RANDAO_MIXES_LENGTH
assert slot <= state.slot
state.latest_randao_mixes[slot mod LATEST_RANDAO_MIXES_LENGTH]
func get_attestation_participants*(state: BeaconState,
attestation_data: AttestationData,
aggregation_bitfield: seq[byte]): seq[Uint24] =
@ -315,6 +293,8 @@ func process_ejections*(state: var BeaconState) =
func update_validator_registry*(state: var BeaconState) =
let
current_epoch = get_current_epoch(state)
next_epoch = current_epoch + 1
active_validator_indices =
get_active_validator_indices(state.validator_registry, state.slot)
# The total effective balance of active validators
@ -329,7 +309,7 @@ func update_validator_registry*(state: var BeaconState) =
# Activate validators within the allowable balance churn
var balance_churn = 0'u64
for index, validator in state.validator_registry:
if validator.activation_slot > state.slot + ENTRY_EXIT_DELAY and
if validator.activation_epoch > get_entry_exit_effect_epoch(current_epoch) and
state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.Uint24)
@ -342,7 +322,7 @@ func update_validator_registry*(state: var BeaconState) =
# Exit validators within the allowable balance churn
balance_churn = 0
for index, validator in state.validator_registry:
if (validator.exit_slot > state.slot + ENTRY_EXIT_DELAY) and
if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and
((validator.status_flags and INITIATED_EXIT) == INITIATED_EXIT):
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.Uint24)
@ -352,18 +332,20 @@ func update_validator_registry*(state: var BeaconState) =
# Exit validator
exit_validator(state, index.Uint24)
state.validator_registry_update_epoch = current_epoch
# Perform additional updates
state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot
state.previous_epoch_start_shard = state.current_epoch_start_shard
state.previous_epoch_randao_mix = state.current_epoch_randao_mix
state.current_epoch_calculation_slot = state.slot
state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) mod SHARD_COUNT
state.current_epoch_randao_mix = get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD)
state.current_calculation_epoch = next_epoch
state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count(state)) mod SHARD_COUNT
state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)
# TODO "If a validator registry update does not happen do the following: ..."
process_penalties_and_exits(state)
func get_epoch_start_slot*(epoch: EpochNumber): SlotNumber =
epoch * EPOCH_LENGTH
proc checkAttestation*(
state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool =
## Check that an attestation follows the rules of being included in the state
@ -382,20 +364,20 @@ proc checkAttestation*(
attestation_slot = attestation.data.slot, state_slot = state.slot)
return
let expected_justified_slot =
if attestation.data.slot >= state.slot - (state.slot mod EPOCH_LENGTH):
state.justified_slot
let expected_justified_epoch =
if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)):
state.justified_epoch
else:
state.previous_justified_slot
state.previous_justified_epoch
if not (attestation.data.justified_slot == expected_justified_slot):
warn("Unexpected justified slot",
attestation_justified_slot = attestation.data.justified_slot,
expected_justified_slot)
if not (attestation.data.justified_epoch == expected_justified_epoch):
warn("Unexpected justified epoch",
attestation_justified_epoch = attestation.data.justified_epoch,
expected_justified_epoch)
return
let expected_justified_block_root =
get_block_root(state, attestation.data.justified_slot)
get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))
if not (attestation.data.justified_block_root == expected_justified_block_root):
warn("Unexpected justified block root",
attestation_justified_block_root = attestation.data.justified_block_root,
@ -420,7 +402,7 @@ proc checkAttestation*(
if not bls_verify(
group_public_key, @(msg.data) & @[0'u8], attestation.aggregate_signature,
get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION)
get_domain(state.fork, attestation.data.slot, DOMAIN_ATTESTATION)
):
warn("Invalid attestation group signature")
return

View File

@ -75,9 +75,6 @@ const
BEACON_CHAIN_SHARD_NUMBER* = not 0'u64 # 2^64 - 1 in spec
MAX_CASPER_VOTES* = 2^10
LATEST_BLOCK_ROOTS_LENGTH* = 2'u64^13
LATEST_RANDAO_MIXES_LENGTH* = 2'u64^13
LATEST_PENALIZED_EXIT_LENGTH* = 8192 # epochs
MAX_WITHDRAWALS_PER_EPOCH* = 4 # withdrawals
# Deposit contract
@ -89,12 +86,19 @@ const
MAX_DEPOSIT_AMOUNT* = 2'u64^5 * GWEI_PER_ETH ##\
## Maximum amounth of ETH that can be deposited in one call
# Time parameter, here so that GENESIS_EPOCH can access it
EPOCH_LENGTH* = 64 ##\
## (~6.4 minutes)
## slots that make up an epoch, at the end of which more heavy
## processing is done
# Initial values
GENESIS_FORK_VERSION* = 0'u64
GENESIS_SLOT* = 0'u64
GENESIS_SLOT* = 2'u64^19
GENESIS_EPOCH* = GENESIS_SLOT div EPOCH_LENGTH # slot_to_epoch(GENESIS_SLOT)
GENESIS_START_SHARD* = 0'u64
FAR_FUTURE_SLOT* = not 0'u64 # 2^64 - 1 in spec
FAR_FUTURE_EPOCH* = not 0'u64 # 2^64 - 1 in spec
ZERO_HASH* = Eth2Digest()
# TODO EMPTY_SIGNATURE* =
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
@ -115,11 +119,6 @@ const
## wait towards the end of the slot and still have time to publish the
## attestation.
EPOCH_LENGTH* = 64 ##\
## (~6.4 minutes)
## slots that make up an epoch, at the end of which more heavy
## processing is done
ETH1_DATA_VOTING_PERIOD* = 2'u64^10 ##\
## slots (~1.7 hours)
@ -138,6 +137,12 @@ const
MIN_VALIDATOR_WITHDRAWAL_TIME* = 2'u64^14 ##\
## slots (~27 hours)
# State list lengths
LATEST_BLOCK_ROOTS_LENGTH* = 2'u64^13
LATEST_RANDAO_MIXES_LENGTH* = 2'u64^13
LATEST_INDEX_ROOTS_LENGTH* = 2'u64^13
LATEST_PENALIZED_EXIT_LENGTH* = 8192 # epochs
# Quotients
BASE_REWARD_QUOTIENT* = 2'u64^10 ##\
## The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It
@ -161,6 +166,8 @@ const
type
Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around
SlotNumber* = uint64
EpochNumber* = uint64
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#data-structures
ProposerSlashing* = object
@ -210,8 +217,8 @@ type
latest_crosslink_root*: Eth2Digest ##\
## Last crosslink hash
justified_slot*: uint64 ##\
## Slot of last justified beacon block
justified_epoch*: uint64 ##\
## Epoch of last justified beacon block
justified_block_root*: Eth2Digest ##\
## Hash of last justified beacon block
@ -279,17 +286,9 @@ type
proposer_slashings*: seq[ProposerSlashing]
casper_slashings*: seq[CasperSlashing]
attestations*: seq[Attestation]
custody_reseeds*: seq[CustodyReseed]
custody_challenges*: seq[CustodyChallenge]
custody_responses*: seq[CustodyResponse]
deposits*: seq[Deposit]
exits*: seq[Exit]
# Phase1:
CustodyReseed* = object
CustodyChallenge* = object
CustodyResponse* = object
ProposalSignedData* = object
slot*: uint64
shard*: uint64 ##\
@ -299,7 +298,7 @@ type
BeaconState* = object
slot*: uint64
genesis_time*: uint64
fork_data*: ForkData ##\
fork*: Fork ##\
## For versioning hard forks
# Validator registry
@ -307,36 +306,33 @@ type
validator_balances*: seq[uint64] ##\
## Validator balances in Gwei!
validator_registry_update_slot*: uint64
validator_registry_update_epoch*: uint64
validator_registry_exit_count*: uint64
# TODO remove, not in spec anymore
validator_registry_delta_chain_tip*: Eth2Digest ##\
## For light clients to easily track delta
# Randomness and committees
latest_randao_mixes*: array[LATEST_BLOCK_ROOTS_LENGTH.int, Eth2Digest]
latest_vdf_outputs*: array[
(LATEST_RANDAO_MIXES_LENGTH div EPOCH_LENGTH).int, Eth2Digest]
previous_epoch_start_shard*: uint64
current_epoch_start_shard*: uint64
previous_epoch_calculation_slot*: uint64
current_epoch_calculation_slot*: uint64
previous_epoch_randao_mix*: Eth2Digest
current_epoch_randao_mix*: Eth2Digest
# Custody challenges
custody_challenges*: seq[CustodyChallenge]
previous_calculation_epoch*: EpochNumber
current_calculation_epoch*: EpochNumber
previous_epoch_seed*: Eth2Digest
current_epoch_seed*: Eth2Digest
# Finality
previous_justified_slot*: uint64
justified_slot*: uint64
previous_justified_epoch*: EpochNumber
justified_epoch*: EpochNumber
justification_bitfield*: uint64
finalized_slot*: uint64
finalized_epoch*: EpochNumber
# Recent state
latest_crosslinks*: array[SHARD_COUNT, Crosslink]
latest_block_roots*: array[LATEST_BLOCK_ROOTS_LENGTH.int, Eth2Digest] ##\
## Needed to process attestations, older to newer
latest_index_roots*: array[LATEST_INDEX_ROOTS_LENGTH.int, Eth2Digest]
latest_penalized_exit_balances*: seq[uint64] ##\
## Balances penalized in the current withdrawal period
@ -350,6 +346,8 @@ type
Validator* = object
pubkey*: ValidatorPubKey
withdrawal_credentials*: Eth2Digest
# TODO remove randao_commitment, randao_layers, latest_status_change_slot, custody_commitment, latest_custody_reseed_slot, penultimate_custody_reseed_slot
randao_commitment*: Eth2Digest ##\
## RANDAO commitment created by repeatedly taking the hash of a secret value
## so as to create "onion layers" around it. For every block that a
@ -365,16 +363,16 @@ type
latest_status_change_slot*: uint64 ##\
## Slot when validator last changed status (or 0)
activation_slot*: uint64 ##\
activation_epoch*: EpochNumber ##\
## Slot when validator activated
exit_slot*: uint64 ##\
exit_epoch*: EpochNumber ##\
## Slot when validator exited
withdrawal_slot*: uint64 ##\
withdrawal_epoch*: EpochNumber ##\
## Slot when validator withdrew
penalized_slot*: uint64 ##\
penalized_epoch*: EpochNumber ##\
## Slot when validator penalized
exit_count*: uint64 ##\
@ -391,7 +389,7 @@ type
## Slot of second-latest custody reseed
Crosslink* = object
slot*: uint64
epoch*: uint64
shard_block_root*: Eth2Digest ##\
## Shard chain block root
@ -421,7 +419,7 @@ type
custody_bitfield*: seq[byte] # Proof of custody bitfield
slot_included*: uint64 # Slot in which it was included
ForkData* = object
Fork* = object
pre_fork_version*: uint64 # Previous fork version
post_fork_version*: uint64 # Post fork version
fork_slot*: uint64 # Fork slot number

View File

@ -119,14 +119,14 @@ func integer_squareroot*(n: SomeInteger): SomeInteger =
y = (x + n div x) div 2
x
func get_fork_version*(fork_data: ForkData, slot: uint64): uint64 =
if slot < fork_data.fork_slot: fork_data.pre_fork_version
else: fork_data.post_fork_version
func get_fork_version*(fork: Fork, slot: uint64): uint64 =
if slot < fork.fork_slot: fork.pre_fork_version
else: fork.post_fork_version
func get_domain*(
fork_data: ForkData, slot: uint64, domain_type: SignatureDomain): uint64 =
fork: Fork, slot: uint64, domain_type: SignatureDomain): uint64 =
# TODO Slot overflow? Or is slot 32 bits for all intents and purposes?
(get_fork_version(fork_data, slot) shl 32) + domain_type.uint32
(get_fork_version(fork, slot) shl 32) + domain_type.uint32
func is_power_of_2*(v: uint64): bool = (v and (v-1)) == 0
@ -147,6 +147,9 @@ proc is_double_vote*(attestation_data_1: AttestationData,
## same slot - doing so means risking getting slashed.
attestation_data_1.slot == attestation_data_2.slot
func slot_to_epoch*(slot: SlotNumber): EpochNumber =
slot div EPOCH_LENGTH
proc is_surround_vote*(attestation_data_1: AttestationData,
attestation_data_2: AttestationData): bool =
## Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
@ -154,30 +157,65 @@ proc is_surround_vote*(attestation_data_1: AttestationData,
## due to a 'surround vote'.
## Note: parameter order matters as this function only checks
## that ``attestation_data_1`` surrounds ``attestation_data_2``.
let
source_epoch_1 = attestation_data_1.justified_epoch
source_epoch_2 = attestation_data_2.justified_epoch
target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
(
(attestation_data_1.justified_slot < attestation_data_2.justified_slot) and
(attestation_data_1.justified_slot + 1 == attestation_data_2.slot) and
(attestation_data_2.slot < attestation_data_1.slot)
(source_epoch_1 < source_epoch_2) and
(source_epoch_2 + 1 == target_epoch_2) and
(target_epoch_2 < target_epoch_1)
)
func is_active_validator*(validator: Validator, slot: uint64): bool =
func is_active_validator*(validator: Validator, epoch: EpochNumber): bool =
### Checks if validator is active
validator.activation_slot <= slot and slot < validator.exit_slot
validator.activation_epoch <= epoch and epoch < validator.exit_epoch
func get_active_validator_indices*(validators: openArray[Validator], slot: uint64): seq[Uint24] =
# TODO Uint24 -> ValidatorIndex
func get_active_validator_indices*(validators: openArray[Validator], epoch: EpochNumber): seq[Uint24] =
## Gets indices of active validators from validators
for idx, val in validators:
if is_active_validator(val, slot):
if is_active_validator(val, epoch):
result.add idx.Uint24
func get_committee_count_per_slot*(active_validator_count: int): uint64 =
func get_epoch_committee_count*(active_validator_count: int): uint64 =
clamp(
active_validator_count div EPOCH_LENGTH div TARGET_COMMITTEE_SIZE,
1, SHARD_COUNT div EPOCH_LENGTH).uint64
1, SHARD_COUNT div EPOCH_LENGTH).uint64 * EPOCH_LENGTH
func get_current_epoch_committee_count_per_slot*(state: BeaconState): uint64 =
func get_current_epoch_committee_count*(state: BeaconState): uint64 =
let current_active_validators = get_active_validator_indices(
state.validator_registry,
state.current_epoch_calculation_slot,
state.current_calculation_epoch,
)
return get_committee_count_per_slot(len(current_active_validators))
return get_epoch_committee_count(len(current_active_validators))
func get_current_epoch*(state: BeaconState): EpochNumber =
slot_to_epoch(state.slot)
## TODO I've been moving things into helpers because of layering issues
## but create DAG of which helper functions ref others and topo sort to
## refactor all of this mess.
func get_randao_mix*(state: BeaconState,
epoch: EpochNumber): Eth2Digest =
## Returns the randao mix at a recent ``epoch``.
assert get_current_epoch(state) - LATEST_RANDAO_MIXES_LENGTH < epoch
assert epoch <= get_current_epoch(state)
state.latest_randao_mixes[epoch mod LATEST_RANDAO_MIXES_LENGTH]
func get_active_index_root(state: BeaconState, epoch: EpochNumber): Eth2Digest =
# Returns the index root at a recent ``epoch``.
assert get_current_epoch(state) - LATEST_INDEX_ROOTS_LENGTH < epoch
assert epoch <= get_current_epoch(state)
state.latest_index_roots[epoch mod LATEST_INDEX_ROOTS_LENGTH]
func generate_seed*(state: BeaconState, epoch: EpochNumber): Eth2Digest =
# Generate a seed for the given ``epoch``.
var seed_input : array[32*2, byte]
seed_input[0..31] = get_randao_mix(state, epoch - SEED_LOOKAHEAD).data
seed_input[32..63] = get_active_index_root(state, epoch).data
eth2hash(seed_input)

View File

@ -30,9 +30,10 @@ func xorSeed(seed: Eth2Digest, x: uint64): Eth2Digest =
for i in 0 ..< 8:
result.data[31 - i] = result.data[31 - i] xor byte((x shr i*8) and 0xff)
# TODO Uint24 -> ValidatorIndex
func get_shuffling*(seed: Eth2Digest,
validators: openArray[Validator],
slot_nonaligned: uint64
epoch: EpochNumber
): 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
@ -40,20 +41,18 @@ func get_shuffling*(seed: Eth2Digest,
## 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, epoch)
active_validator_indices = get_active_validator_indices(validators, slot)
committees_per_slot = get_committee_count_per_slot(len(active_validator_indices)).int
committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)).int
# Shuffle
shuffled_active_validator_indices = shuffle(
active_validator_indices,
xorSeed(seed, slot))
xorSeed(seed, epoch))
# 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..
# Split the shuffled list into committees_per_epoch pieces
result = split(shuffled_active_validator_indices, committees_per_epoch)
assert result.len() == committees_per_epoch # what split should do..
func get_new_validator_registry_delta_chain_tip*(
current_validator_registry_delta_chain_tip: Eth2Digest,
@ -71,60 +70,65 @@ func get_new_validator_registry_delta_chain_tip*(
flag: flag
))
func get_previous_epoch_committee_count_per_slot(state: BeaconState): uint64 =
func get_previous_epoch_committee_count(state: BeaconState): uint64 =
let previous_active_validators = get_active_validator_indices(
state.validator_registry,
state.previous_epoch_calculation_slot
state.previous_calculation_epoch,
)
get_committee_count_per_slot(len(previous_active_validators))
get_epoch_committee_count(len(previous_active_validators))
func get_current_epoch_committee_count_per_slot(state: BeaconState): uint64 =
let previous_active_validators = get_active_validator_indices(
let current_active_validators = get_active_validator_indices(
state.validator_registry,
state.current_epoch_calculation_slot
state.current_calculation_epoch,
)
get_committee_count_per_slot(len(previous_active_validators))
get_epoch_committee_count(len(current_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
let
epoch = slot_to_epoch(slot)
current_epoch = get_current_epoch(state)
previous_epoch = if current_epoch > GENESIS_EPOCH: (current_epoch - 1) else: current_epoch
next_epoch = current_epoch + 1
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
assert previous_epoch <= epoch
assert epoch < next_epoch
## 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
func get_epoch_specific_params() : auto =
if epoch < current_epoch:
let
committees_per_epoch = get_previous_epoch_committee_count(state)
seed = state.previous_epoch_seed
shuffling_epoch = state.previous_calculation_epoch
shuffling_start_shard = state.previous_epoch_start_shard
(committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard)
else:
let
committees_per_epoch = get_current_epoch_committee_count(state)
seed = state.current_epoch_seed
shuffling_epoch = state.current_calculation_epoch
shuffling_start_shard = state.current_epoch_start_shard
(committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard)
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
)
let (committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard) = get_epoch_specific_params()
let
shuffling = get_shuffling(
seed,
state.validator_registry,
shuffling_epoch,
)
offset = slot mod EPOCH_LENGTH
committees_per_slot = committees_per_epoch div EPOCH_LENGTH
slot_start_shard = (shuffling_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
)
func get_shard_committees_at_slot*(
state: BeaconState, slot: uint64): seq[ShardCommittee] =

View File

@ -62,7 +62,7 @@ func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
bls_verify(
state.validator_registry[proposer_index].pubkey,
proposal_hash.data, blck.signature,
get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))
get_domain(state.fork, state.slot, DOMAIN_PROPOSAL))
proc processRandao(
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
@ -127,7 +127,7 @@ func penalizeValidator(state: var BeaconState, index: Uint24) =
whistleblower_reward = get_effective_balance(state, index) div WHISTLEBLOWER_REWARD_QUOTIENT
state.validator_balances[whistleblower_index] += whistleblower_reward
state.validator_balances[index] -= whistleblower_reward
validator.penalized_slot = state.slot
validator.penalized_epoch = get_current_epoch(state)
proc processProposerSlashings(
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
@ -146,7 +146,7 @@ proc processProposerSlashings(
hash_tree_root_final(proposer_slashing.proposal_data_1).data,
proposer_slashing.proposal_signature_1,
get_domain(
state.fork_data, proposer_slashing.proposal_data_1.slot,
state.fork, proposer_slashing.proposal_data_1.slot,
DOMAIN_PROPOSAL)):
notice "PropSlash: invalid signature 1"
return false
@ -155,7 +155,7 @@ proc processProposerSlashings(
hash_tree_root_final(proposer_slashing.proposal_data_2).data,
proposer_slashing.proposal_signature_2,
get_domain(
state.fork_data, proposer_slashing.proposal_data_2.slot,
state.fork, proposer_slashing.proposal_data_2.slot,
DOMAIN_PROPOSAL)):
notice "PropSlash: invalid signature 2"
return false
@ -175,7 +175,7 @@ proc processProposerSlashings(
notice "PropSlash: block root mismatch"
return false
if not (proposer.penalized_slot > state.slot):
if not (proposer.penalized_epoch > get_current_epoch(state)):
notice "PropSlash: penalized slot"
return false
@ -240,7 +240,7 @@ proc processCasperSlashings(state: var BeaconState, blck: BeaconBlock): bool =
return false
for i in intersection:
if state.validator_registry[i].penalized_slot > state.slot:
if state.validator_registry[i].penalized_epoch > get_current_epoch(state):
penalize_validator(state, i)
return true
@ -296,11 +296,11 @@ proc processExits(
if skipValidation notin flags:
if not bls_verify(
validator.pubkey, ZERO_HASH.data, exit.signature,
get_domain(state.fork_data, exit.slot, DOMAIN_EXIT)):
get_domain(state.fork, exit.slot, DOMAIN_EXIT)):
notice "Exit: invalid signature"
return false
if not (validator.exit_slot > state.slot + ENTRY_EXIT_DELAY):
if not (validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))):
notice "Exit: exit/entry too close"
return false
@ -419,7 +419,7 @@ func boundary_attestations(
# TODO spec - add as helper?
attestations.filterIt(
it.data.epoch_boundary_root == boundary_hash and
it.data.justified_slot == state.justified_slot)
it.data.justified_epoch == state.justified_epoch)
func lowerThan(candidate, current: Eth2Digest): bool =
# return true iff candidate is "lower" than current, per spec rule:
@ -458,6 +458,10 @@ func processEpoch(state: var BeaconState) =
base_reward_quotient =
BASE_REWARD_QUOTIENT * integer_squareroot(total_balance_in_eth)
current_epoch = get_current_epoch(state)
previous_epoch = if current_epoch > GENESIS_EPOCH: current_epoch - 1 else: current_epoch
next_epoch = (current_epoch + 1).EpochNumber
func base_reward(state: BeaconState, index: Uint24): uint64 =
get_effective_balance(state, index) div base_reward_quotient.uint64 div 4
@ -499,7 +503,7 @@ func processEpoch(state: var BeaconState) =
let # Previous epoch justified
previous_epoch_justified_attestations =
concat(current_epoch_attestations, previous_epoch_attestations).
filterIt(it.data.justified_slot == state.previous_justified_slot)
filterIt(it.data.justified_epoch == state.previous_justified_epoch)
previous_epoch_justified_attester_indices =
get_attester_indices(state, previous_epoch_justified_attestations)
@ -592,50 +596,55 @@ func processEpoch(state: var BeaconState) =
break
state.eth1_data_votes = @[]
# TODO Eth1 data
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#eth1-data-1
# Helpers for justification
let
previous_total_balance = sum_effective_balances(state, get_active_validator_indices(state.validator_registry, previous_epoch))
current_total_balance = sum_effective_balances(state, get_active_validator_indices(state.validator_registry, current_epoch))
block: # Justification
state.previous_justified_slot = state.justified_slot
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#justification
# TODO where's that bitfield type when you need it?
# TODO why are all bits kept?
# First, update the justification bitfield
var new_justified_epoch = state.justified_epoch
state.justification_bitfield = state.justification_bitfield shl 1
if 3'u64 * previous_epoch_boundary_attesting_balance >= 2'u64 * previous_total_balance:
state.justification_bitfield = state.justification_bitfield or 2
new_justified_epoch = previous_epoch
if 3'u64 * current_epoch_boundary_attesting_balance >= 2'u64 * current_total_balance:
state.justification_bitfield = state.justification_bitfield or 1
new_justified_epoch = current_epoch
# TODO Spec - underflow
if state.slot >= 2'u64 * EPOCH_LENGTH:
if 3'u64 * previous_epoch_boundary_attesting_balance >=
2'u64 * total_balance:
state.justification_bitfield = state.justification_bitfield or 2
state.justified_slot = state.slot - 2 * EPOCH_LENGTH
# Next, update last finalized epoch if possible
if (state.justification_bitfield shr 1) mod 8 == 0b111 and state.previous_justified_epoch == previous_epoch - 2:
state.finalized_epoch = state.previous_justified_epoch
if (state.justification_bitfield shr 1) mod 4 == 0b11 and state.previous_justified_epoch == previous_epoch - 1:
state.finalized_epoch = state.previous_justified_epoch
if (state.justification_bitfield shr 0) mod 8 == 0b111 and state.justified_epoch == previous_epoch - 1:
state.finalized_epoch = state.justified_epoch
if (state.justification_bitfield shr 0) mod 4 == 0b11 and state.justified_epoch == previous_epoch:
state.finalized_epoch = state.justified_epoch
if 3'u64 * current_epoch_boundary_attesting_balance >=
2'u64 * total_balance:
state.justification_bitfield = state.justification_bitfield or 1
state.justified_slot = state.slot - 1 * EPOCH_LENGTH
block: # Finalization
if
(state.previous_justified_slot + 2 * EPOCH_LENGTH == state.slot and
state.justification_bitfield mod 4 == 3) or
(state.previous_justified_slot + 3 * EPOCH_LENGTH == state.slot and
state.justification_bitfield mod 8 == 7) or
(state.previous_justified_slot + 4 * EPOCH_LENGTH == state.slot and
state.justification_bitfield mod 16 in [15'u64, 14]):
state.finalized_slot = state.justified_slot
# Finally, update the following
state.previous_justified_epoch = state.justified_epoch
state.justified_epoch = new_justified_epoch
block: # Crosslinks
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)
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks
for slot in get_epoch_start_slot(previous_epoch) ..< get_epoch_start_slot(next_epoch):
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))
# TODO Rewards and penalties helpers
block: # Justification and finalization
let
epochs_since_finality =
(state.slot - state.finalized_slot) div EPOCH_LENGTH
let epochs_since_finality = next_epoch - state.finalized_epoch
proc update_balance(attesters: openArray[Uint24], attesting_balance: uint64) =
# TODO Spec - add helper?
@ -692,7 +701,8 @@ func processEpoch(state: var BeaconState) =
base_reward(state, v) div INCLUDER_REWARD_QUOTIENT
block: # Crosslinks
for slot in state.slot - 2 * EPOCH_LENGTH ..< state.slot - EPOCH_LENGTH:
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1
for slot in get_epoch_start_slot(previous_epoch) ..< get_epoch_start_slot(current_epoch):
let crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)
for crosslink_committee in crosslink_committees_at_slot:
# TODO https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1
@ -707,26 +717,29 @@ func processEpoch(state: var BeaconState) =
block: # Ejections
process_ejections(state)
block: # Validator registry
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#validator-registry
state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot
block: # Validator registry and shuffling seed data
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
state.previous_calculation_epoch = state.current_calculation_epoch
state.previous_epoch_start_shard = state.current_epoch_start_shard
state.previous_epoch_randao_mix = state.current_epoch_randao_mix
state.previous_epoch_seed = state.current_epoch_seed
#TODO state.latest_index_roots[next_epoch mod LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root_final(get_active_validator_indices(state.validator_registry, next_epoch))
if state.finalized_slot > state.validator_registry_update_slot and
if state.finalized_epoch > state.validator_registry_update_epoch and
allIt(
0 ..< get_current_epoch_committee_count_per_slot(state).int * EPOCH_LENGTH,
state.latest_crosslinks[(state.current_epoch_start_shard + it.uint64) mod SHARD_COUNT].slot > state.validator_registry_update_slot):
0 ..< get_current_epoch_committee_count(state).int * EPOCH_LENGTH,
state.latest_crosslinks[(state.current_epoch_start_shard + it.uint64) mod SHARD_COUNT].epoch > state.validator_registry_update_epoch):
update_validator_registry(state)
state.current_epoch_calculation_slot = state.slot
state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) mod SHARD_COUNT
state.current_epoch_randao_mix = get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD)
state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count(state) * EPOCH_LENGTH) mod SHARD_COUNT
state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)
state.current_calculation_epoch = next_epoch
else:
# If a validator registry change does NOT happen
let epochs_since_last_registry_change = (state.slot - state.validator_registry_update_slot) div EPOCH_LENGTH
let epochs_since_last_registry_change = current_epoch - state.validator_registry_update_epoch
if is_power_of_2(epochs_since_last_registry_change):
state.current_epoch_calculation_slot = state.slot
state.current_epoch_randao_mix = get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD)
state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)
state.current_calculation_epoch = next_epoch
# /Note/ that state.current_epoch_start_shard is left unchanged
# TODO run process_penalties_and_exits
block: # Final updates

View File

@ -85,7 +85,7 @@ proc applyValidatorChangeLog*(log: ChangeLog,
# 4. Apply all changes to the validator set
#
outBeaconState.finalized_slot =
outBeaconState.finalized_epoch =
log.signedBlock.slot div EPOCH_LENGTH
outBeaconState.validator_registry_delta_chain_tip =

View File

@ -38,7 +38,7 @@ proc randomTimeInSlot*(s: BeaconState,
proc slotDistanceFromNow*(s: BeaconState): int64 =
## Returns how many slots have passed since a particular BeaconState was finalized
int64(s.timeSinceGenesis() div (SLOT_DURATION * 1000)) - int64(s.finalized_slot)
int64(s.timeSinceGenesis() div (SLOT_DURATION * EPOCH_LENGTH * 1000)) - int64(s.finalized_epoch)
proc synchronizeClock*() {.async.} =
## This should determine the offset of the local clock against a global

View File

@ -109,7 +109,7 @@ suite "Block processing":
# Some time needs to pass before attestations are included - this is
# to let the attestation propagate properly to interested participants
while state.slot < MIN_ATTESTATION_INCLUSION_DELAY + 1:
while state.slot < GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1:
discard updateState(
state, previous_block_root, none(BeaconBlock), {})

View File

@ -25,12 +25,12 @@ suite "Validators":
num_validators = 32*1024
validators = repeat(
Validator(
exit_slot: FAR_FUTURE_SLOT
exit_epoch: FAR_FUTURE_EPOCH
), num_validators)
s = get_shuffling(Eth2Digest(), validators, 0)
committees = EPOCH_LENGTH * get_committee_count_per_slot(len(validators)).int
committees = get_epoch_committee_count(len(validators)).int
check:
s.len == committees
# 32k validators: EPOCH_LENGTH slots * committee_count_per_slot =
# EPOCH_LENGTH * committee_count_per_slot committees.
# get_epoch_committee_count committees.
sumCommittees(s, num_validators div committees) == validators.len() # all validators accounted for

View File

@ -152,7 +152,7 @@ proc addBlock*(
assert bls_verify(
proposer.pubkey,
proposal_hash, new_block.signature,
get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL)),
get_domain(state.fork, state.slot, DOMAIN_PROPOSAL)),
"we just signed this message - it should pass verification!"
new_block
@ -189,8 +189,8 @@ proc makeAttestation*(
epoch_boundary_root: Eth2Digest(), # TODO
shard_block_root: Eth2Digest(), # TODO
latest_crosslink_root: Eth2Digest(), # TODO
justified_slot: state.justified_slot,
justified_block_root: get_block_root(state, state.justified_slot),
justified_epoch: state.justified_epoch,
justified_block_root: get_block_root(state, get_epoch_start_slot(state.justified_epoch)),
)
assert sac_index != -1, "find_shard_committe should guarantee this"