2021-01-18 20:34:41 +00:00
|
|
|
# beacon_chain
|
2021-02-15 16:40:00 +00:00
|
|
|
# Copyright (c) 2020-2021 Status Research & Development GmbH
|
2021-01-18 20:34:41 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
import
|
|
|
|
ssz/types,
|
|
|
|
spec/[datatypes, digest, helpers]
|
|
|
|
|
|
|
|
func diffModIncEpoch[T, U](hl: HashArray[U, T], startSlot: uint64):
|
|
|
|
array[SLOTS_PER_EPOCH, T] =
|
|
|
|
static: doAssert U.uint64 mod SLOTS_PER_EPOCH == 0
|
|
|
|
doAssert startSlot mod SLOTS_PER_EPOCH == 0
|
|
|
|
for i in startSlot ..< startSlot + SLOTS_PER_EPOCH:
|
|
|
|
result[i mod SLOTS_PER_EPOCH] = hl[i mod U.uint64]
|
|
|
|
|
|
|
|
func applyModIncrement[T, U](
|
|
|
|
ha: var HashArray[U, T], hl: array[SLOTS_PER_EPOCH, T], slot: uint64) =
|
|
|
|
var indexSlot = slot
|
|
|
|
|
|
|
|
for item in hl:
|
|
|
|
ha[indexSlot mod U.uint64] = item
|
|
|
|
indexSlot += 1
|
|
|
|
|
|
|
|
func applyValidatorIdentities(
|
|
|
|
validators: var HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT],
|
|
|
|
hl: auto) =
|
|
|
|
for item in hl:
|
|
|
|
validators.add Validator(
|
|
|
|
pubkey: item.pubkey,
|
|
|
|
withdrawal_credentials: item.withdrawal_credentials)
|
|
|
|
|
|
|
|
func setValidatorStatuses(
|
|
|
|
validators: var HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT],
|
|
|
|
hl: List[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT]) =
|
|
|
|
doAssert validators.len == hl.len
|
|
|
|
|
|
|
|
for i in 0 ..< hl.len:
|
|
|
|
validators[i].effective_balance = hl[i].effective_balance
|
|
|
|
validators[i].slashed = hl[i].slashed
|
|
|
|
|
|
|
|
validators[i].activation_eligibility_epoch =
|
|
|
|
hl[i].activation_eligibility_epoch
|
|
|
|
validators[i].activation_epoch = hl[i].activation_epoch
|
|
|
|
validators[i].exit_epoch = hl[i].exit_epoch
|
|
|
|
validators[i].withdrawable_epoch = hl[i].withdrawable_epoch
|
|
|
|
|
|
|
|
func deltaEncodeBalances*[T, U](balances: HashList[T, U]): List[T, U] =
|
|
|
|
if balances.len == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
result.add balances[0]
|
|
|
|
|
|
|
|
for i in 1 ..< balances.len:
|
|
|
|
result.add balances[i] - balances[i - 1]
|
|
|
|
|
|
|
|
doAssert balances.len == result.len
|
|
|
|
|
|
|
|
func deltaDecodeBalances*[T, U](encodedBalances: List[T, U]): HashList[T, U] =
|
|
|
|
var accum = 0'u64
|
|
|
|
for i in 0 ..< encodedBalances.len:
|
|
|
|
accum += encodedBalances[i]
|
|
|
|
result.add accum
|
|
|
|
|
|
|
|
doAssert encodedBalances.len == result.len
|
|
|
|
|
|
|
|
func replaceOrAddEncodeEth1Votes[T, U](votes0, votes1: HashList[T, U]):
|
|
|
|
(bool, List[T, U]) =
|
|
|
|
let
|
|
|
|
num_votes0 = votes0.len
|
|
|
|
lower_bound =
|
|
|
|
if votes1.len < num_votes0 or
|
|
|
|
(num_votes0 > 0 and votes0[num_votes0 - 1] != votes1[num_votes0 - 1]):
|
|
|
|
# EPOCHS_PER_ETH1_VOTING_PERIOD epochs have passed, and
|
|
|
|
# eth1_data_votes has been reset/cleared. Because their
|
|
|
|
# deposit_index counts increase monotonically, it works
|
|
|
|
# to use only the last element for comparison.
|
|
|
|
0
|
|
|
|
else:
|
|
|
|
num_votes0
|
|
|
|
|
|
|
|
result[0] = lower_bound == 0
|
|
|
|
for i in lower_bound ..< votes1.len:
|
|
|
|
result[1].add votes1[i]
|
|
|
|
|
|
|
|
func replaceOrAddDecodeEth1Votes[T, U](
|
|
|
|
votes0: var HashList[T, U], eth1_data_votes_replaced: bool,
|
|
|
|
votes1: List[T, U]) =
|
|
|
|
if eth1_data_votes_replaced:
|
|
|
|
votes0 = HashList[T, U]()
|
|
|
|
|
|
|
|
for item in votes1:
|
|
|
|
votes0.add item
|
|
|
|
|
2021-02-15 16:40:00 +00:00
|
|
|
func getMutableValidatorStatus(validator: Validator): ValidatorStatus =
|
|
|
|
ValidatorStatus(
|
|
|
|
effective_balance: validator.effective_balance,
|
|
|
|
slashed: validator.slashed,
|
|
|
|
activation_eligibility_epoch: validator.activation_eligibility_epoch,
|
|
|
|
activation_epoch: validator.activation_epoch,
|
|
|
|
exit_epoch: validator.exit_epoch,
|
|
|
|
withdrawable_epoch: validator.withdrawable_epoch)
|
|
|
|
|
|
|
|
func getMutableValidatorStatuses(state: BeaconState):
|
|
|
|
List[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT] =
|
|
|
|
# use mapIt + .init(foo)?
|
|
|
|
for validator in state.validators:
|
|
|
|
result.add getMutableValidatorStatus(validator)
|
|
|
|
|
2021-01-18 20:34:41 +00:00
|
|
|
func diffStates*(state0, state1: BeaconState): BeaconStateDiff =
|
|
|
|
doAssert state1.slot > state0.slot
|
|
|
|
doAssert state0.slot.isEpoch
|
|
|
|
doAssert state1.slot == state0.slot + SLOTS_PER_EPOCH
|
|
|
|
# TODO not here, but in chainDag, an isancestorof check
|
|
|
|
|
|
|
|
doAssert state0.genesis_time == state1.genesis_time
|
|
|
|
doAssert state0.genesis_validators_root == state1.genesis_validators_root
|
|
|
|
doAssert state0.fork == state1.fork
|
|
|
|
doAssert state1.historical_roots.len - state0.historical_roots.len in [0, 1]
|
|
|
|
|
|
|
|
let
|
|
|
|
historical_root_added =
|
|
|
|
state0.historical_roots.len != state1.historical_roots.len
|
|
|
|
(eth1_data_votes_replaced, eth1_data_votes) =
|
|
|
|
replaceOrAddEncodeEth1Votes(state0.eth1_data_votes, state1.eth1_data_votes)
|
|
|
|
|
|
|
|
BeaconStateDiff(
|
|
|
|
slot: state1.slot,
|
|
|
|
latest_block_header: state1.latest_block_header,
|
|
|
|
|
|
|
|
block_roots: diffModIncEpoch(state1.block_roots, state0.slot.uint64),
|
|
|
|
state_roots: diffModIncEpoch(state1.state_roots, state0.slot.uint64),
|
|
|
|
historical_root_added: historical_root_added,
|
|
|
|
historical_root:
|
|
|
|
if historical_root_added:
|
|
|
|
state1.historical_roots[state0.historical_roots.len]
|
|
|
|
else:
|
|
|
|
default(Eth2Digest),
|
|
|
|
eth1_data: state1.eth1_data,
|
|
|
|
eth1_data_votes_replaced: eth1_data_votes_replaced,
|
|
|
|
eth1_data_votes: eth1_data_votes,
|
|
|
|
eth1_deposit_index: state1.eth1_deposit_index,
|
|
|
|
|
2021-02-15 16:40:00 +00:00
|
|
|
validatorStatuses: getMutableValidatorStatuses(state1),
|
2021-01-18 20:34:41 +00:00
|
|
|
balances: deltaEncodeBalances(state1.balances),
|
|
|
|
|
|
|
|
# RANDAO mixes gets updated every block, in place
|
|
|
|
randao_mix: state1.randao_mixes[state0.slot.compute_epoch_at_slot.uint64 mod
|
|
|
|
EPOCHS_PER_HISTORICAL_VECTOR.uint64],
|
|
|
|
slashing: state1.slashings[state0.slot.compute_epoch_at_slot.uint64 mod
|
|
|
|
EPOCHS_PER_HISTORICAL_VECTOR.uint64],
|
|
|
|
|
|
|
|
previous_epoch_attestations: state1.previous_epoch_attestations,
|
|
|
|
current_epoch_attestations: state1.current_epoch_attestations,
|
|
|
|
|
|
|
|
justification_bits: state1.justification_bits,
|
|
|
|
previous_justified_checkpoint: state1.previous_justified_checkpoint,
|
|
|
|
current_justified_checkpoint: state1.current_justified_checkpoint,
|
|
|
|
finalized_checkpoint: state1.finalized_checkpoint
|
|
|
|
)
|
|
|
|
|
|
|
|
func applyDiff*(
|
|
|
|
state: var BeaconState,
|
|
|
|
immutableValidators: openArray[ImmutableValidatorData],
|
|
|
|
stateDiff: BeaconStateDiff) =
|
|
|
|
# Carry over unchanged genesis_time, genesis_validators_root, and fork.
|
|
|
|
state.latest_block_header = stateDiff.latest_block_header
|
|
|
|
|
|
|
|
applyModIncrement(state.block_roots, stateDiff.block_roots, state.slot.uint64)
|
|
|
|
applyModIncrement(state.state_roots, stateDiff.state_roots, state.slot.uint64)
|
|
|
|
if stateDiff.historical_root_added:
|
|
|
|
state.historical_roots.add stateDiff.historical_root
|
|
|
|
|
|
|
|
state.eth1_data = stateDiff.eth1_data
|
|
|
|
replaceOrAddDecodeEth1Votes(
|
|
|
|
state.eth1_data_votes, stateDiff.eth1_data_votes_replaced,
|
|
|
|
stateDiff.eth1_data_votes)
|
|
|
|
state.eth1_deposit_index = stateDiff.eth1_deposit_index
|
|
|
|
|
|
|
|
applyValidatorIdentities(state.validators, immutableValidators)
|
|
|
|
setValidatorStatuses(state.validators, stateDiff.validator_statuses)
|
|
|
|
state.balances = deltaDecodeBalances(stateDiff.balances)
|
|
|
|
|
|
|
|
# RANDAO mixes gets updated every block, in place, so ensure there's always
|
|
|
|
# >=1 value from it
|
|
|
|
let epochIndex =
|
|
|
|
state.slot.epoch.uint64 mod EPOCHS_PER_HISTORICAL_VECTOR.uint64
|
|
|
|
state.randao_mixes[epochIndex] = stateDiff.randao_mix
|
|
|
|
state.slashings[epochIndex] = stateDiff.slashing
|
|
|
|
|
|
|
|
state.previous_epoch_attestations = stateDiff.previous_epoch_attestations
|
|
|
|
state.current_epoch_attestations = stateDiff.current_epoch_attestations
|
|
|
|
|
|
|
|
state.justification_bits = stateDiff.justification_bits
|
|
|
|
state.previous_justified_checkpoint = stateDiff.previous_justified_checkpoint
|
|
|
|
state.current_justified_checkpoint = stateDiff.current_justified_checkpoint
|
|
|
|
state.finalized_checkpoint = stateDiff.finalized_checkpoint
|
|
|
|
|
|
|
|
# Don't update slot until the end, because various other updates depend on it
|
|
|
|
state.slot = stateDiff.slot
|