commit
33dd0da1de
|
@ -95,7 +95,7 @@ proc sync*(node: BeaconNode): Future[bool] {.async.} =
|
|||
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)
|
||||
|
|
|
@ -10,7 +10,7 @@ import
|
|||
../extras, ../ssz,
|
||||
./crypto, ./datatypes, ./digest, ./helpers, ./validator
|
||||
|
||||
func get_effective_balance*(state: BeaconState, index: Uint24): uint64 =
|
||||
func get_effective_balance*(state: BeaconState, index: ValidatorIndex): uint64 =
|
||||
# Validators collect rewards which increases their balance but not their
|
||||
# influence. Validators may also lose balance if they fail to do their duty
|
||||
# in which case their influence decreases. Once they drop below a certain
|
||||
|
@ -18,7 +18,7 @@ func get_effective_balance*(state: BeaconState, index: Uint24): uint64 =
|
|||
min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
|
||||
|
||||
func sum_effective_balances*(
|
||||
state: BeaconState, validator_indices: openArray[Uint24]): uint64 =
|
||||
state: BeaconState, validator_indices: openArray[ValidatorIndex]): uint64 =
|
||||
# TODO spec - add as helper? Used pretty often
|
||||
for index in validator_indices:
|
||||
result += get_effective_balance(state, index)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -50,8 +50,7 @@ func process_deposit(state: var BeaconState,
|
|||
deposit: uint64,
|
||||
proof_of_possession: ValidatorSig,
|
||||
withdrawal_credentials: Eth2Digest,
|
||||
randao_commitment: Eth2Digest,
|
||||
custody_commitment: Eth2Digest) : Uint24 =
|
||||
randao_commitment: Eth2Digest) : ValidatorIndex =
|
||||
## Process a deposit from Ethereum 1.0.
|
||||
|
||||
if false:
|
||||
|
@ -70,15 +69,12 @@ 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,
|
||||
latest_custody_reseed_slot: GENESIS_SLOT,
|
||||
penultimate_custody_reseed_slot: GENESIS_SLOT
|
||||
)
|
||||
|
||||
let index = min_empty_validator_index(
|
||||
|
@ -86,11 +82,11 @@ func process_deposit(state: var BeaconState,
|
|||
if index.isNone():
|
||||
state.validator_registry.add(validator)
|
||||
state.validator_balances.add(deposit)
|
||||
(len(state.validator_registry) - 1).Uint24
|
||||
(len(state.validator_registry) - 1).ValidatorIndex
|
||||
else:
|
||||
state.validator_registry[index.get()] = validator
|
||||
state.validator_balances[index.get()] = deposit
|
||||
index.get().Uint24
|
||||
index.get().ValidatorIndex
|
||||
else:
|
||||
# Increase balance by deposit amount
|
||||
let index = validator_pubkeys.find(pubkey)
|
||||
|
@ -99,89 +95,71 @@ func process_deposit(state: var BeaconState,
|
|||
withdrawal_credentials
|
||||
|
||||
state.validator_balances[index] += deposit
|
||||
index.Uint24
|
||||
index.ValidatorIndex
|
||||
|
||||
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
|
||||
|
||||
func activate_validator(state: var BeaconState,
|
||||
index: Uint24,
|
||||
index: ValidatorIndex,
|
||||
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) =
|
||||
index: ValidatorIndex) =
|
||||
## Initiate exit for the validator with the given ``index``.
|
||||
var validator = state.validator_registry[index]
|
||||
validator.status_flags = validator.status_flags or INITIATED_EXIT
|
||||
state.validator_registry[index] = validator
|
||||
|
||||
func exit_validator*(state: var BeaconState,
|
||||
index: Uint24) =
|
||||
index: ValidatorIndex) =
|
||||
## Exit the validator with the given ``index``.
|
||||
|
||||
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) =
|
||||
let
|
||||
current_epoch = get_current_epoch(state)
|
||||
# The active validators
|
||||
let active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)
|
||||
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
|
||||
penalty = get_effective_balance(state, index.Uint24) * min(total_penalties * 3, total_balance) div total_balance
|
||||
penalty = get_effective_balance(state, index.ValidatorIndex) * min(total_penalties * 3, total_balance) div total_balance
|
||||
state.validator_balances[index] -= penalty
|
||||
|
||||
## 'state' is of type <var BeaconState> which cannot be captured as it
|
||||
## would violate memory safety, when using nested function approach in
|
||||
## spec directly. That said, the spec approach evidently is not meant,
|
||||
## based on its abundant and pointless memory copies, for production.
|
||||
var eligible_indices : seq[Uint24] = @[]
|
||||
var eligible_indices : seq[ValidatorIndex] = @[]
|
||||
for i in 0 ..< len(state.validator_registry):
|
||||
eligible_indices.add i.Uint24
|
||||
eligible_indices.add i.ValidatorIndex
|
||||
|
||||
## TODO figure out that memory safety issue, which would come up again when
|
||||
## sorting, and then actually do withdrawals
|
||||
|
@ -211,29 +189,30 @@ func get_initial_beacon_state*(
|
|||
# Misc
|
||||
slot: GENESIS_SLOT,
|
||||
genesis_time: genesis_time,
|
||||
fork_data: ForkData(
|
||||
pre_fork_version: GENESIS_FORK_VERSION,
|
||||
post_fork_version: GENESIS_FORK_VERSION,
|
||||
# rm fork_slot init in favor of epoch
|
||||
fork: Fork(
|
||||
previous_version: GENESIS_FORK_VERSION,
|
||||
current_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,
|
||||
|
@ -248,7 +227,6 @@ func get_initial_beacon_state*(
|
|||
deposit.deposit_data.deposit_input.proof_of_possession,
|
||||
deposit.deposit_data.deposit_input.withdrawal_credentials,
|
||||
deposit.deposit_data.deposit_input.randao_commitment,
|
||||
deposit.deposit_data.deposit_input.custody_commitment,
|
||||
)
|
||||
|
||||
if state.validator_balances[validator_index] >= MAX_DEPOSIT_AMOUNT:
|
||||
|
@ -256,7 +234,7 @@ func get_initial_beacon_state*(
|
|||
|
||||
# Process initial activations
|
||||
for validator_index in 0 ..< state.validator_registry.len:
|
||||
let vi = validator_index.Uint24
|
||||
let vi = validator_index.ValidatorIndex
|
||||
if get_effective_balance(state, vi) > MAX_DEPOSIT_AMOUNT:
|
||||
activate_validator(state, vi, true)
|
||||
|
||||
|
@ -268,16 +246,9 @@ 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] =
|
||||
aggregation_bitfield: seq[byte]): seq[ValidatorIndex] =
|
||||
## Attestation participants in the attestation data are called out in a
|
||||
## bit field that corresponds to the committee of the shard at the time - this
|
||||
## function converts it to list of indices in to BeaconState.validators
|
||||
|
@ -292,7 +263,7 @@ func get_attestation_participants*(state: BeaconState,
|
|||
# TODO investigate functional library / approach to help avoid loop bugs
|
||||
assert any(
|
||||
crosslink_committees,
|
||||
func (x: tuple[a: seq[Uint24], b: uint64]): bool = x[1] == attestation_data.shard)
|
||||
func (x: tuple[a: seq[ValidatorIndex], b: uint64]): bool = x[1] == attestation_data.shard)
|
||||
let crosslink_committee = mapIt(
|
||||
filterIt(crosslink_committees, it.b == attestation_data.shard),
|
||||
it.a)[0]
|
||||
|
@ -315,6 +286,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,41 +302,43 @@ 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)
|
||||
balance_churn += get_effective_balance(state, index.ValidatorIndex)
|
||||
if balance_churn > max_balance_churn:
|
||||
break
|
||||
|
||||
# Activate validator
|
||||
activate_validator(state, index.Uint24, false)
|
||||
activate_validator(state, index.ValidatorIndex, false)
|
||||
|
||||
# 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)
|
||||
balance_churn += get_effective_balance(state, index.ValidatorIndex)
|
||||
if balance_churn > max_balance_churn:
|
||||
break
|
||||
|
||||
# Exit validator
|
||||
exit_validator(state, index.Uint24)
|
||||
exit_validator(state, index.ValidatorIndex)
|
||||
|
||||
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 +357,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 +395,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
|
||||
|
|
|
@ -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
|
||||
|
@ -160,11 +165,13 @@ const
|
|||
MAX_EXITS* = 2^4
|
||||
|
||||
type
|
||||
Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around
|
||||
ValidatorIndex* = 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
|
||||
proposer_index*: Uint24
|
||||
proposer_index*: ValidatorIndex
|
||||
proposal_data_1*: ProposalSignedData
|
||||
proposal_signature_1*: ValidatorSig
|
||||
proposal_data_2*: ProposalSignedData
|
||||
|
@ -175,10 +182,10 @@ type
|
|||
slashable_vote_data_2*: SlashableVoteData
|
||||
|
||||
SlashableVoteData* = object
|
||||
aggregate_signature_poc_0_indices*: seq[Uint24] ##\
|
||||
aggregate_signature_poc_0_indices*: seq[ValidatorIndex] ##\
|
||||
## Proof-of-custody indices (0 bits)
|
||||
|
||||
aggregate_signature_poc_1_indices*: seq[Uint24] ##\
|
||||
aggregate_signature_poc_1_indices*: seq[ValidatorIndex] ##\
|
||||
## Proof-of-custody indices (1 bits)
|
||||
|
||||
data*: AttestationData
|
||||
|
@ -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
|
||||
|
@ -247,7 +254,7 @@ type
|
|||
# Minimum slot for processing exit
|
||||
slot*: uint64
|
||||
# Index of the exiting validator
|
||||
validator_index*: Uint24
|
||||
validator_index*: ValidatorIndex
|
||||
# Validator signature
|
||||
signature*: ValidatorSig
|
||||
|
||||
|
@ -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
|
||||
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 ##\
|
||||
|
@ -382,22 +380,14 @@ type
|
|||
|
||||
status_flags*: uint64
|
||||
|
||||
custody_commitment*: Eth2Digest
|
||||
|
||||
latest_custody_reseed_slot*: uint64 ##\
|
||||
## Slot of latest custody reseed
|
||||
|
||||
penultimate_custody_reseed_slot*: uint64 ##\
|
||||
## Slot of second-latest custody reseed
|
||||
|
||||
Crosslink* = object
|
||||
slot*: uint64
|
||||
epoch*: uint64
|
||||
shard_block_root*: Eth2Digest ##\
|
||||
## Shard chain block root
|
||||
|
||||
ShardCommittee* = object
|
||||
shard*: uint64
|
||||
committee*: seq[Uint24] ##\
|
||||
committee*: seq[ValidatorIndex] ##\
|
||||
## Committe participants that get to attest to blocks on this shard -
|
||||
## indices into BeaconState.validator_registry
|
||||
|
||||
|
@ -421,14 +411,14 @@ type
|
|||
custody_bitfield*: seq[byte] # Proof of custody bitfield
|
||||
slot_included*: uint64 # Slot in which it was included
|
||||
|
||||
ForkData* = object
|
||||
pre_fork_version*: uint64 # Previous fork version
|
||||
post_fork_version*: uint64 # Post fork version
|
||||
fork_slot*: uint64 # Fork slot number
|
||||
Fork* = object
|
||||
previous_version*: uint64 # Previous fork version
|
||||
current_version*: uint64 # Current fork version
|
||||
fork_slot*: uint64 # Fork slot number TODO should be epoch
|
||||
|
||||
ValidatorRegistryDeltaBlock* = object
|
||||
latest_registry_delta_root*: Eth2Digest
|
||||
validator_index*: Uint24
|
||||
validator_index*: ValidatorIndex
|
||||
pubkey*: ValidatorPubKey
|
||||
slot*: uint64
|
||||
flag*: ValidatorSetDeltaFlags
|
||||
|
@ -457,10 +447,10 @@ when true:
|
|||
proc read*(rlp: var Rlp, T: type ValidatorPubKey): T {.inline.} =
|
||||
discard
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: Uint24) =
|
||||
proc append*(rlpWriter: var RlpWriter, value: ValidatorIndex) =
|
||||
discard
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Uint24): T {.inline.} =
|
||||
proc read*(rlp: var Rlp, T: type ValidatorIndex): T {.inline.} =
|
||||
discard
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: ValidatorSig) =
|
||||
|
|
|
@ -53,9 +53,9 @@ func shuffle*[T](values: seq[T], seed: Eth2Digest): seq[T] =
|
|||
|
||||
# Read 3-bytes of `source` as a 24-bit big-endian integer.
|
||||
let sample_from_source =
|
||||
source.data[pos].Uint24 shl 16 or
|
||||
source.data[pos+1].Uint24 shl 8 or
|
||||
source.data[pos+2].Uint24
|
||||
source.data[pos].ValidatorIndex shl 16 or
|
||||
source.data[pos+1].ValidatorIndex shl 8 or
|
||||
source.data[pos+2].ValidatorIndex
|
||||
|
||||
# Sample values greater than or equal to `sample_max` will cause
|
||||
# modulo bias when mapped into the `remaining` range.
|
||||
|
@ -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.previous_version
|
||||
else: fork.current_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,64 @@ 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] =
|
||||
func get_active_validator_indices*(validators: openArray[Validator], epoch: EpochNumber): seq[ValidatorIndex] =
|
||||
## Gets indices of active validators from validators
|
||||
for idx, val in validators:
|
||||
if is_active_validator(val, slot):
|
||||
result.add idx.Uint24
|
||||
if is_active_validator(val, epoch):
|
||||
result.add idx.ValidatorIndex
|
||||
|
||||
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) + 1
|
||||
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)
|
||||
|
|
|
@ -32,32 +32,30 @@ func xorSeed(seed: Eth2Digest, x: uint64): Eth2Digest =
|
|||
|
||||
func get_shuffling*(seed: Eth2Digest,
|
||||
validators: openArray[Validator],
|
||||
slot_nonaligned: uint64
|
||||
): seq[seq[Uint24]] =
|
||||
epoch: EpochNumber
|
||||
): seq[seq[ValidatorIndex]] =
|
||||
## 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, 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,
|
||||
index: Uint24,
|
||||
index: ValidatorIndex,
|
||||
pubkey: ValidatorPubKey,
|
||||
slot: uint64,
|
||||
flag: ValidatorSetDeltaFlags): Eth2Digest =
|
||||
|
@ -71,54 +69,59 @@ 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]] =
|
||||
func get_crosslink_committees_at_slot*(state: BeaconState, slot: uint64) : seq[tuple[a: seq[ValidatorIndex], 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
|
||||
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
|
||||
|
||||
## 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
|
||||
)
|
||||
assert previous_epoch <= epoch
|
||||
assert epoch < next_epoch
|
||||
|
||||
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_slot = get_current_epoch_committee_count_per_slot(state)
|
||||
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)
|
||||
|
||||
let (committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard) = get_epoch_specific_params()
|
||||
|
||||
let
|
||||
shuffling = get_shuffling(
|
||||
state.current_epoch_randao_mix,
|
||||
seed,
|
||||
state.validator_registry,
|
||||
state.current_epoch_calculation_slot
|
||||
shuffling_epoch,
|
||||
)
|
||||
slot_start_shard = (state.current_epoch_start_shard + committees_per_slot * offset) mod SHARD_COUNT
|
||||
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 (
|
||||
|
@ -129,7 +132,7 @@ func get_crosslink_committees_at_slot*(state: BeaconState, slot: uint64) : seq[t
|
|||
func get_shard_committees_at_slot*(
|
||||
state: BeaconState, slot: uint64): seq[ShardCommittee] =
|
||||
# TODO temporary adapter; remove when all users gone
|
||||
# where ShardCommittee is: shard*: uint64 / committee*: seq[Uint24]
|
||||
# where ShardCommittee is: shard*: uint64 / committee*: seq[ValidatorIndex]
|
||||
let index = state.get_shard_committees_index(slot)
|
||||
#state.shard_committees_at_slots[index]
|
||||
for crosslink_committee in get_crosslink_committees_at_slot(state, slot):
|
||||
|
@ -138,7 +141,7 @@ func get_shard_committees_at_slot*(
|
|||
sac.committee = crosslink_committee.a
|
||||
result.add sac
|
||||
|
||||
func get_beacon_proposer_index*(state: BeaconState, slot: uint64): Uint24 =
|
||||
func get_beacon_proposer_index*(state: BeaconState, slot: uint64): ValidatorIndex =
|
||||
## From Casper RPJ mini-spec:
|
||||
## When slot i begins, validator Vidx is expected
|
||||
## to create ("propose") a block, which contains a pointer to some parent block
|
||||
|
|
|
@ -28,7 +28,7 @@ func toBytesSSZ(x: SomeInteger): array[sizeof(x), byte] =
|
|||
elif x.sizeof == 1: copyMem(result.addr, x.unsafeAddr, sizeof(result))
|
||||
else: {.fatal: "Unsupported type serialization: " & $(type(x)).name.}
|
||||
|
||||
func toBytesSSZ(x: Uint24): array[3, byte] =
|
||||
func toBytesSSZ(x: ValidatorIndex): array[3, byte] =
|
||||
## Integers are all encoded as little endian and not padded
|
||||
let v = x.uint32
|
||||
result[0] = byte(v and 0xff)
|
||||
|
@ -52,13 +52,13 @@ type
|
|||
# provides the actual nim-type-to-bytes conversion.
|
||||
# TODO think about this for a bit - depends where the serialization of
|
||||
# validator keys ends up going..
|
||||
# TODO can't put ranges like Uint24 in here:
|
||||
# TODO can't put ranges like ValidatorIndex in here:
|
||||
# https://github.com/nim-lang/Nim/issues/10027
|
||||
SomeInteger | EthAddress | Eth2Digest | ValidatorPubKey | ValidatorSig |
|
||||
bool
|
||||
|
||||
func sszLen(v: TrivialTypes): int = toBytesSSZ(v).len
|
||||
func sszLen(v: Uint24): int = toBytesSSZ(v).len
|
||||
func sszLen(v: ValidatorIndex): int = toBytesSSZ(v).len
|
||||
|
||||
func sszLen(v: object | tuple): int =
|
||||
result = 4 # Length
|
||||
|
@ -94,14 +94,14 @@ func fromBytesSSZUnsafe(T: typedesc[bool], data: pointer): T =
|
|||
# definition for now, but maybe this should be a parse error instead?
|
||||
fromBytesSSZUnsafe(uint8, data) != 0
|
||||
|
||||
func fromBytesSSZUnsafe(T: typedesc[Uint24], data: pointer): T =
|
||||
func fromBytesSSZUnsafe(T: typedesc[ValidatorIndex], data: pointer): T =
|
||||
## Integers are all encoded as littleendian and not padded
|
||||
var tmp: uint32
|
||||
let p = cast[ptr UncheckedArray[byte]](data)
|
||||
tmp = tmp or uint32(p[0])
|
||||
tmp = tmp or uint32(p[1]) shl 8
|
||||
tmp = tmp or uint32(p[2]) shl 16
|
||||
result = tmp.Uint24
|
||||
result = tmp.ValidatorIndex
|
||||
|
||||
func fromBytesSSZUnsafe(T: typedesc[EthAddress], data: pointer): T =
|
||||
copyMem(result.addr, data, sizeof(result))
|
||||
|
@ -127,11 +127,11 @@ proc deserialize[T: TrivialTypes](
|
|||
true
|
||||
|
||||
func deserialize(
|
||||
dest: var Uint24, offset: var int, data: openArray[byte]): bool =
|
||||
dest: var ValidatorIndex, offset: var int, data: openArray[byte]): bool =
|
||||
if offset + sszLen(dest) > data.len():
|
||||
false
|
||||
else:
|
||||
dest = fromBytesSSZUnsafe(Uint24, data[offset].unsafeAddr)
|
||||
dest = fromBytesSSZUnsafe(ValidatorIndex, data[offset].unsafeAddr)
|
||||
offset += sszLen(dest)
|
||||
true
|
||||
|
||||
|
@ -146,7 +146,7 @@ func deserialize[T: enum](dest: var T, offset: var int, data: openArray[byte]):
|
|||
dest = cast[T](tmp)
|
||||
true
|
||||
|
||||
proc deserialize[T: not (enum|TrivialTypes|Uint24)](
|
||||
proc deserialize[T: not (enum|TrivialTypes|ValidatorIndex)](
|
||||
dest: var T, offset: var int, data: openArray[byte]): bool =
|
||||
# Length in bytes, followed by each item
|
||||
var totalLen: uint32
|
||||
|
@ -176,7 +176,7 @@ proc deserialize[T: not (enum|TrivialTypes|Uint24)](
|
|||
|
||||
func serialize(dest: var seq[byte], src: TrivialTypes) =
|
||||
dest.add src.toBytesSSZ()
|
||||
func serialize(dest: var seq[byte], src: Uint24) =
|
||||
func serialize(dest: var seq[byte], src: ValidatorIndex) =
|
||||
dest.add src.toBytesSSZ()
|
||||
|
||||
func serialize(dest: var seq[byte], x: enum) =
|
||||
|
@ -265,7 +265,7 @@ func hash_tree_root*(x: SomeInteger | bool): array[sizeof(x), byte] =
|
|||
## All integers are serialized as **little endian**.
|
||||
toBytesSSZ(x)
|
||||
|
||||
func hash_tree_root*(x: Uint24): array[3, byte] =
|
||||
func hash_tree_root*(x: ValidatorIndex): array[3, byte] =
|
||||
## Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``)
|
||||
## All integers are serialized as **little endian**.
|
||||
toBytesSSZ(x)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
# * We mix procedural and functional styles for no good reason, except that the
|
||||
# spec does so also.
|
||||
# * There are no tests, and likely lots of bugs.
|
||||
# * For indices, we get a mix of uint64, Uint24 and int - this is currently
|
||||
# * For indices, we get a mix of uint64, ValidatorIndex and int - this is currently
|
||||
# swept under the rug with casts
|
||||
# * The spec uses uint64 for data types, but functions in the spec often assume
|
||||
# signed bigint semantics - under- and overflows ensue
|
||||
|
@ -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 =
|
||||
|
@ -117,17 +117,17 @@ func processDepositRoot(state: var BeaconState, blck: BeaconBlock) =
|
|||
vote_count: 1
|
||||
)
|
||||
|
||||
func penalizeValidator(state: var BeaconState, index: Uint24) =
|
||||
func penalizeValidator(state: var BeaconState, index: ValidatorIndex) =
|
||||
exit_validator(state, index)
|
||||
var validator = state.validator_registry[index]
|
||||
#state.latest_penalized_exit_balances[(state.slot div EPOCH_LENGTH) mod LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index.Uint24)
|
||||
#state.latest_penalized_exit_balances[(state.slot div EPOCH_LENGTH) mod LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index.ValidatorIndex)
|
||||
|
||||
let
|
||||
whistleblower_index = get_beacon_proposer_index(state, state.slot)
|
||||
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
|
||||
|
||||
|
@ -199,7 +199,7 @@ func verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVoteData
|
|||
|
||||
return true
|
||||
|
||||
proc indices(vote: SlashableVoteData): seq[Uint24] =
|
||||
proc indices(vote: SlashableVoteData): seq[ValidatorIndex] =
|
||||
vote.aggregate_signature_poc_0_indices &
|
||||
vote.aggregate_signature_poc_1_indices
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -321,7 +321,7 @@ proc process_ejections(state: var BeaconState) =
|
|||
for index, validator in state.validator_registry:
|
||||
if is_active_validator(validator, state.slot) and
|
||||
state.validator_balances[index] < EJECTION_BALANCE:
|
||||
exit_validator(state, index.Uint24)
|
||||
exit_validator(state, index.ValidatorIndex)
|
||||
|
||||
func processSlot(state: var BeaconState, previous_block_root: Eth2Digest) =
|
||||
## Time on the beacon chain moves in slots. Every time we make it to a new
|
||||
|
@ -403,7 +403,7 @@ proc processBlock(
|
|||
|
||||
func get_attester_indices(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestationRecord]): seq[Uint24] =
|
||||
attestations: openArray[PendingAttestationRecord]): seq[ValidatorIndex] =
|
||||
# Union of attesters that participated in some attestations
|
||||
# TODO spec - add as helper?
|
||||
attestations.
|
||||
|
@ -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:
|
||||
|
@ -429,13 +429,13 @@ func lowerThan(candidate, current: Eth2Digest): bool =
|
|||
if v > candidate.data[i]: return true
|
||||
return false
|
||||
|
||||
func inclusion_slot(state: BeaconState, v: Uint24): uint64 =
|
||||
func inclusion_slot(state: BeaconState, v: ValidatorIndex): uint64 =
|
||||
for a in state.latest_attestations:
|
||||
if v in get_attestation_participants(state, a.data, a.participation_bitfield):
|
||||
return a.slot_included
|
||||
doAssert false # shouldn't happen..
|
||||
|
||||
func inclusion_distance(state: BeaconState, v: Uint24): uint64 =
|
||||
func inclusion_distance(state: BeaconState, v: ValidatorIndex): uint64 =
|
||||
for a in state.latest_attestations:
|
||||
if v in get_attestation_participants(state, a.data, a.participation_bitfield):
|
||||
return a.slot_included - a.data.slot
|
||||
|
@ -458,11 +458,15 @@ func processEpoch(state: var BeaconState) =
|
|||
base_reward_quotient =
|
||||
BASE_REWARD_QUOTIENT * integer_squareroot(total_balance_in_eth)
|
||||
|
||||
func base_reward(state: BeaconState, index: Uint24): uint64 =
|
||||
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: ValidatorIndex): uint64 =
|
||||
get_effective_balance(state, index) div base_reward_quotient.uint64 div 4
|
||||
|
||||
func inactivity_penalty(
|
||||
state: BeaconState, index: Uint24, epochs_since_finality: uint64): uint64 =
|
||||
state: BeaconState, index: ValidatorIndex, epochs_since_finality: uint64): uint64 =
|
||||
base_reward(state, index) +
|
||||
get_effective_balance(state, index) *
|
||||
epochs_since_finality div INACTIVITY_PENALTY_QUOTIENT div 2
|
||||
|
@ -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)
|
||||
|
@ -538,14 +542,14 @@ func processEpoch(state: var BeaconState) =
|
|||
# these closures outside this scope, but still..
|
||||
let statePtr = state.addr
|
||||
func attesting_validator_indices(
|
||||
crosslink_committee: tuple[a: seq[Uint24], b: uint64], shard_block_root: Eth2Digest): seq[Uint24] =
|
||||
crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64], shard_block_root: Eth2Digest): seq[ValidatorIndex] =
|
||||
let shard_block_attestations =
|
||||
concat(current_epoch_attestations, previous_epoch_attestations).
|
||||
filterIt(it.data.shard == crosslink_committee.b and
|
||||
it.data.shard_block_root == shard_block_root)
|
||||
get_attester_indices(statePtr[], shard_block_attestations)
|
||||
|
||||
func winning_root(crosslink_committee: tuple[a: seq[Uint24], b: uint64]): Eth2Digest =
|
||||
func winning_root(crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64]): Eth2Digest =
|
||||
# * Let `winning_root(crosslink_committee)` be equal to the value of
|
||||
# `shard_block_root` such that
|
||||
# `sum([get_effective_balance(state, i) for i in attesting_validator_indices(crosslink_committee, shard_block_root)])`
|
||||
|
@ -571,17 +575,17 @@ func processEpoch(state: var BeaconState) =
|
|||
max_val = val
|
||||
max_hash
|
||||
|
||||
func attesting_validators(crosslink_committee: tuple[a: seq[Uint24], b: uint64]): seq[Uint24] =
|
||||
func attesting_validators(crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64]): seq[ValidatorIndex] =
|
||||
attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))
|
||||
|
||||
func attesting_validator_indices(crosslink_committee: tuple[a: seq[Uint24], b: uint64]): seq[Uint24] =
|
||||
func attesting_validator_indices(crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64]): seq[ValidatorIndex] =
|
||||
attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))
|
||||
|
||||
func total_attesting_balance(crosslink_committee: tuple[a: seq[Uint24], b: uint64]): uint64 =
|
||||
func total_attesting_balance(crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64]): uint64 =
|
||||
sum_effective_balances(
|
||||
statePtr[], attesting_validator_indices(crosslink_committee))
|
||||
|
||||
func total_balance_sac(crosslink_committee: tuple[a: seq[Uint24], b: uint64]): uint64 =
|
||||
func total_balance_sac(crosslink_committee: tuple[a: seq[ValidatorIndex], b: uint64]): uint64 =
|
||||
sum_effective_balances(statePtr[], crosslink_committee.a)
|
||||
|
||||
block: # Eth1 data
|
||||
|
@ -592,52 +596,57 @@ 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
|
||||
|
||||
# TODO Spec - underflow
|
||||
if state.slot >= 2'u64 * EPOCH_LENGTH:
|
||||
if 3'u64 * previous_epoch_boundary_attesting_balance >=
|
||||
2'u64 * total_balance:
|
||||
if 3'u64 * previous_epoch_boundary_attesting_balance >= 2'u64 * previous_total_balance:
|
||||
state.justification_bitfield = state.justification_bitfield or 2
|
||||
state.justified_slot = state.slot - 2 * EPOCH_LENGTH
|
||||
|
||||
if 3'u64 * current_epoch_boundary_attesting_balance >=
|
||||
2'u64 * total_balance:
|
||||
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
|
||||
state.justified_slot = state.slot - 1 * EPOCH_LENGTH
|
||||
new_justified_epoch = current_epoch
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
# 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))
|
||||
|
||||
block: # Justification and finalization
|
||||
let
|
||||
epochs_since_finality =
|
||||
(state.slot - state.finalized_slot) div EPOCH_LENGTH
|
||||
# TODO Rewards and penalties helpers
|
||||
|
||||
proc update_balance(attesters: openArray[Uint24], attesting_balance: uint64) =
|
||||
block: # Justification and finalization
|
||||
let epochs_since_finality = next_epoch - state.finalized_epoch
|
||||
|
||||
proc update_balance(attesters: openArray[ValidatorIndex], attesting_balance: uint64) =
|
||||
# TODO Spec - add helper?
|
||||
for v in attesters:
|
||||
statePtr.validator_balances[v] +=
|
||||
|
@ -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_calculation_epoch = next_epoch
|
||||
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)
|
||||
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_calculation_epoch = next_epoch
|
||||
state.current_epoch_seed = generate_seed(state, state.current_calculation_epoch)
|
||||
# /Note/ that state.current_epoch_start_shard is left unchanged
|
||||
# TODO run process_penalties_and_exits
|
||||
|
||||
block: # Final updates
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,7 +27,7 @@ suite "Simple serialization":
|
|||
f2: EthAddress
|
||||
f3: MDigest[256]
|
||||
f4: seq[byte]
|
||||
f5: Uint24
|
||||
f5: ValidatorIndex
|
||||
|
||||
let expected_deser = Foo(
|
||||
f0: 5,
|
||||
|
@ -35,7 +35,7 @@ suite "Simple serialization":
|
|||
f2: EthAddress.filled(byte 35),
|
||||
f3: MDigest[256].filled(byte 35),
|
||||
f4: @[byte 'c'.ord, 'o'.ord, 'w'.ord],
|
||||
f5: Uint24(79)
|
||||
f5: ValidatorIndex(79)
|
||||
)
|
||||
|
||||
var expected_ser = @[
|
||||
|
@ -61,9 +61,9 @@ suite "Simple serialization":
|
|||
expected_ser[0..^2].deserialize(Foo).isNone()
|
||||
expected_ser[1..^1].deserialize(Foo).isNone()
|
||||
|
||||
test "Uint24 roundtrip":
|
||||
test "ValidatorIndex roundtrip":
|
||||
# https://github.com/nim-lang/Nim/issues/10027
|
||||
let v = 79.Uint24
|
||||
let v = 79.ValidatorIndex
|
||||
let ser = v.serialize()
|
||||
check:
|
||||
ser.len() == 3
|
||||
|
@ -120,4 +120,4 @@ suite "Tree hashing":
|
|||
|
||||
test "Hash integer":
|
||||
check: hash_tree_root(0x01'u32) == [1'u8, 0, 0, 0] # little endian!
|
||||
check: hash_tree_root(Uint24(0x01)) == [1'u8, 0, 0] # little endian!
|
||||
check: hash_tree_root(ValidatorIndex(0x01)) == [1'u8, 0, 0] # little endian!
|
||||
|
|
|
@ -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), {})
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import
|
|||
math,unittest, sequtils,
|
||||
../beacon_chain/spec/[helpers, datatypes, digest, validator]
|
||||
|
||||
func sumCommittees(v: openArray[seq[Uint24]], reqCommitteeLen: int): int =
|
||||
func sumCommittees(v: openArray[seq[ValidatorIndex]], reqCommitteeLen: int): int =
|
||||
for x in v:
|
||||
## This only holds when num_validators is divisible by
|
||||
## EPOCH_LENGTH * get_committee_count_per_slot(len(validators))
|
||||
|
@ -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
|
||||
|
|
|
@ -82,7 +82,7 @@ func makeGenesisBlock*(state: BeaconState): BeaconBlock =
|
|||
state_root: Eth2Digest(data: hash_tree_root(state))
|
||||
)
|
||||
|
||||
func getNextBeaconProposerIndex*(state: BeaconState): Uint24 =
|
||||
func getNextBeaconProposerIndex*(state: BeaconState): ValidatorIndex =
|
||||
# TODO: This is a special version of get_beacon_proposer_index that takes into
|
||||
# account the partial update done at the start of slot processing -
|
||||
# see get_shard_committees_index
|
||||
|
@ -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
|
||||
|
@ -168,14 +168,14 @@ proc makeBlock*(
|
|||
addBlock(next_state, previous_block_root, body)
|
||||
|
||||
proc find_shard_committee(
|
||||
sacs: openArray[ShardCommittee], validator_index: Uint24): ShardCommittee =
|
||||
sacs: openArray[ShardCommittee], validator_index: ValidatorIndex): ShardCommittee =
|
||||
for sac in sacs:
|
||||
if validator_index in sac.committee: return sac
|
||||
doAssert false
|
||||
|
||||
proc makeAttestation*(
|
||||
state: BeaconState, beacon_block_root: Eth2Digest,
|
||||
validator_index: Uint24, flags: UpdateFlags = {}): Attestation =
|
||||
validator_index: ValidatorIndex, flags: UpdateFlags = {}): Attestation =
|
||||
let
|
||||
sac = find_shard_committee(
|
||||
get_shard_committees_at_slot(state, state.slot), validator_index)
|
||||
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue