2019-01-09 23:49:50 +00:00
|
|
|
// Package epoch contains epoch processing libraries. These libraries
|
|
|
|
// process new balance for the validators, justify and finalize new
|
|
|
|
// check points, shuffle and reassign validators to different slots and
|
|
|
|
// shards.
|
2018-12-24 06:36:09 +00:00
|
|
|
package epoch
|
|
|
|
|
|
|
|
import (
|
2018-12-24 14:58:18 +00:00
|
|
|
"fmt"
|
|
|
|
|
2018-12-29 17:24:11 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
2018-12-24 06:36:09 +00:00
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2018-12-31 18:24:52 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
2018-12-24 06:36:09 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
|
|
)
|
|
|
|
|
2019-01-17 01:10:42 +00:00
|
|
|
var config = params.BeaconConfig()
|
|
|
|
|
2018-12-24 06:36:09 +00:00
|
|
|
// CanProcessEpoch checks the eligibility to process epoch.
|
|
|
|
// The epoch can be processed every EPOCH_LENGTH.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// If state.slot % EPOCH_LENGTH == 0:
|
|
|
|
func CanProcessEpoch(state *pb.BeaconState) bool {
|
2019-01-17 01:10:42 +00:00
|
|
|
return state.Slot%config.EpochLength == 0
|
2018-12-24 06:36:09 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 04:42:42 +00:00
|
|
|
// CanProcessDepositRoots checks the eligibility to process deposit root.
|
|
|
|
// The deposit root can be processed every DEPOSIT_ROOT_VOTING_PERIOD.
|
2018-12-24 06:36:09 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-01-09 04:42:42 +00:00
|
|
|
// If state.slot % DEPOSIT_ROOT_VOTING_PERIOD == 0:
|
|
|
|
func CanProcessDepositRoots(state *pb.BeaconState) bool {
|
2019-01-17 01:10:42 +00:00
|
|
|
return state.Slot%config.DepositRootVotingPeriod == 0
|
2018-12-24 06:36:09 +00:00
|
|
|
}
|
|
|
|
|
2018-12-29 18:21:34 +00:00
|
|
|
// CanProcessValidatorRegistry checks the eligibility to process validator registry.
|
2019-01-29 13:56:32 +00:00
|
|
|
// It checks crosslink committees last changed slot and finalized slot against
|
2018-12-29 18:21:34 +00:00
|
|
|
// latest change slot.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// If the following are satisfied:
|
|
|
|
// * state.finalized_slot > state.validator_registry_latest_change_slot
|
|
|
|
// * state.latest_crosslinks[shard].slot > state.validator_registry_latest_change_slot
|
2019-01-29 13:56:32 +00:00
|
|
|
// for every shard number shard in [(state.current_epoch_start_shard + i) %
|
|
|
|
// SHARD_COUNT for i in range(get_current_epoch_committees_per_slot(state) *
|
|
|
|
// EPOCH_LENGTH)] (that is, for every shard in the current committees)
|
2018-12-29 18:21:34 +00:00
|
|
|
func CanProcessValidatorRegistry(state *pb.BeaconState) bool {
|
2019-01-20 18:26:33 +00:00
|
|
|
if state.FinalizedSlot <= state.ValidatorRegistryUpdateSlot {
|
2018-12-29 18:21:34 +00:00
|
|
|
return false
|
|
|
|
}
|
2019-01-29 13:56:32 +00:00
|
|
|
shardsProcessed := validators.CurrCommitteesCountPerSlot(state) * config.EpochLength
|
|
|
|
fmt.Println(shardsProcessed)
|
|
|
|
startShard := state.CurrentEpochStartShard
|
|
|
|
for i := startShard; i < shardsProcessed; i++ {
|
|
|
|
if state.LatestCrosslinks[i%config.ShardCount].Slot <=
|
|
|
|
state.ValidatorRegistryUpdateSlot {
|
|
|
|
return false
|
2018-12-29 18:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-01-09 04:42:42 +00:00
|
|
|
// ProcessDeposits processes deposit roots by checking its vote count.
|
|
|
|
// With sufficient votes (>2*DEPOSIT_ROOT_VOTING_PERIOD), it then
|
2018-12-24 06:36:09 +00:00
|
|
|
// assigns root hash to processed receipt vote in state.
|
2019-01-09 04:42:42 +00:00
|
|
|
func ProcessDeposits(state *pb.BeaconState) *pb.BeaconState {
|
|
|
|
for _, receiptRoot := range state.DepositRootVotes {
|
2019-01-17 01:10:42 +00:00
|
|
|
if receiptRoot.VoteCount*2 > config.DepositRootVotingPeriod {
|
2019-01-09 04:42:42 +00:00
|
|
|
state.LatestDepositRootHash32 = receiptRoot.DepositRootHash32
|
2018-12-24 06:36:09 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-09 04:42:42 +00:00
|
|
|
state.DepositRootVotes = make([]*pb.DepositRootVote, 0)
|
2018-12-24 06:36:09 +00:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessJustification processes for justified slot by comparing
|
|
|
|
// epoch boundary balance and total balance.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// Set state.previous_justified_slot = state.justified_slot.
|
|
|
|
// Set state.justification_bitfield = (state.justification_bitfield * 2) % 2**64.
|
|
|
|
// Set state.justification_bitfield |= 2 and state.justified_slot =
|
2018-12-31 18:24:52 +00:00
|
|
|
// state.slot - 2 * EPOCH_LENGTH if 3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance
|
2018-12-24 06:36:09 +00:00
|
|
|
// Set state.justification_bitfield |= 1 and state.justified_slot =
|
2018-12-31 18:24:52 +00:00
|
|
|
// state.slot - 1 * EPOCH_LENGTH if 3 * this_epoch_boundary_attesting_balance >= 2 * total_balance
|
2018-12-24 06:36:09 +00:00
|
|
|
func ProcessJustification(
|
|
|
|
state *pb.BeaconState,
|
|
|
|
thisEpochBoundaryAttestingBalance uint64,
|
|
|
|
prevEpochBoundaryAttestingBalance uint64,
|
|
|
|
totalBalance uint64) *pb.BeaconState {
|
|
|
|
|
|
|
|
state.PreviousJustifiedSlot = state.JustifiedSlot
|
|
|
|
// Shifts all the bits over one to create a new bit for the recent epoch.
|
|
|
|
state.JustificationBitfield = state.JustificationBitfield * 2
|
|
|
|
|
|
|
|
// If prev prev epoch was justified then we ensure the 2nd bit in the bitfield is set,
|
|
|
|
// assign new justified slot to 2 * EPOCH_LENGTH before.
|
|
|
|
if 3*prevEpochBoundaryAttestingBalance >= 2*totalBalance {
|
|
|
|
state.JustificationBitfield |= 2
|
2019-01-17 01:10:42 +00:00
|
|
|
state.JustifiedSlot = state.Slot - 2*config.EpochLength
|
2018-12-24 06:36:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If this epoch was justified then we ensure the 1st bit in the bitfield is set,
|
|
|
|
// assign new justified slot to 1 * EPOCH_LENGTH before.
|
|
|
|
if 3*thisEpochBoundaryAttestingBalance >= 2*totalBalance {
|
|
|
|
state.JustificationBitfield |= 1
|
2019-01-17 01:10:42 +00:00
|
|
|
state.JustifiedSlot = state.Slot - 1*config.EpochLength
|
2018-12-24 06:36:09 +00:00
|
|
|
}
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessFinalization processes for finalized slot by checking
|
|
|
|
// consecutive justified slots.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// Set state.finalized_slot = state.previous_justified_slot if any of the following are true:
|
|
|
|
// state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3
|
|
|
|
// state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and state.justification_bitfield % 8 == 7
|
|
|
|
// state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and state.justification_bitfield % 16 in (15, 14)
|
|
|
|
func ProcessFinalization(state *pb.BeaconState) *pb.BeaconState {
|
2019-01-17 01:10:42 +00:00
|
|
|
epochLength := config.EpochLength
|
2018-12-24 06:36:09 +00:00
|
|
|
|
|
|
|
if state.PreviousJustifiedSlot == state.Slot-2*epochLength &&
|
|
|
|
state.JustificationBitfield%4 == 3 {
|
|
|
|
state.FinalizedSlot = state.JustifiedSlot
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
if state.PreviousJustifiedSlot == state.Slot-3*epochLength &&
|
|
|
|
state.JustificationBitfield%8 == 7 {
|
|
|
|
state.FinalizedSlot = state.JustifiedSlot
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
if state.PreviousJustifiedSlot == state.Slot-4*epochLength &&
|
|
|
|
(state.JustificationBitfield%16 == 15 ||
|
|
|
|
state.JustificationBitfield%16 == 14) {
|
|
|
|
state.FinalizedSlot = state.JustifiedSlot
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
return state
|
|
|
|
}
|
2018-12-24 14:58:18 +00:00
|
|
|
|
2019-01-25 16:20:02 +00:00
|
|
|
// ProcessCrosslinks goes through each crosslink committee and check
|
|
|
|
// crosslink committee's attested balance * 3 is greater than total balance *2.
|
|
|
|
// If it's greater then beacon node updates crosslink committee with
|
|
|
|
// the state slot and wining root.
|
2018-12-24 14:58:18 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-01-25 16:20:02 +00:00
|
|
|
// For every `slot in range(state.slot - 2 * EPOCH_LENGTH, state.slot)`,
|
|
|
|
// let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`.
|
|
|
|
// For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute:
|
|
|
|
// Set state.latest_crosslinks[shard] = Crosslink(
|
|
|
|
// slot=state.slot, shard_block_root=winning_root(crosslink_committee))
|
|
|
|
// if 3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)
|
2018-12-24 14:58:18 +00:00
|
|
|
func ProcessCrosslinks(
|
|
|
|
state *pb.BeaconState,
|
|
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
|
|
prevEpochAttestations []*pb.PendingAttestationRecord) (*pb.BeaconState, error) {
|
|
|
|
|
2019-01-25 16:20:02 +00:00
|
|
|
var startSlot uint64
|
|
|
|
if state.Slot > 2*config.EpochLength {
|
|
|
|
startSlot = state.Slot - 2*config.EpochLength
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := startSlot; i < state.Slot; i++ {
|
|
|
|
crosslinkCommittees, err := validators.CrosslinkCommitteesAtSlot(state, i)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get committees for slot %d: %v", i, err)
|
|
|
|
}
|
|
|
|
for _, crosslinkCommittee := range crosslinkCommittees {
|
|
|
|
shard := crosslinkCommittee.Shard
|
|
|
|
committee := crosslinkCommittee.Committee
|
|
|
|
attestingBalance, err := TotalAttestingBalance(state, shard, thisEpochAttestations, prevEpochAttestations)
|
2018-12-24 14:58:18 +00:00
|
|
|
if err != nil {
|
2019-01-25 16:20:02 +00:00
|
|
|
return nil, fmt.Errorf("could not get attesting balance for shard committee %d: %v", shard, err)
|
2018-12-24 14:58:18 +00:00
|
|
|
}
|
2019-01-25 16:20:02 +00:00
|
|
|
totalBalance := TotalBalance(state, committee)
|
2018-12-24 14:58:18 +00:00
|
|
|
if attestingBalance*3 > totalBalance*2 {
|
2019-01-25 16:20:02 +00:00
|
|
|
winningRoot, err := winningRoot(state, shard, thisEpochAttestations, prevEpochAttestations)
|
2018-12-24 14:58:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get winning root: %v", err)
|
|
|
|
}
|
2019-01-25 16:20:02 +00:00
|
|
|
state.LatestCrosslinks[shard] = &pb.CrosslinkRecord{
|
2018-12-24 14:58:18 +00:00
|
|
|
Slot: state.Slot,
|
|
|
|
ShardBlockRootHash32: winningRoot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return state, nil
|
|
|
|
}
|
2018-12-29 17:24:11 +00:00
|
|
|
|
|
|
|
// ProcessEjections iterates through every validator and find the ones below
|
|
|
|
// ejection balance and eject them.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// def process_ejections(state: BeaconState) -> None:
|
|
|
|
// """
|
|
|
|
// Iterate through the validator registry
|
|
|
|
// and eject active validators with balance below ``EJECTION_BALANCE``.
|
|
|
|
// """
|
|
|
|
// for index in active_validator_indices(state.validator_registry):
|
|
|
|
// if state.validator_balances[index] < EJECTION_BALANCE:
|
2019-01-05 06:39:07 +00:00
|
|
|
// exit_validator(state, index)
|
2018-12-29 17:24:11 +00:00
|
|
|
func ProcessEjections(state *pb.BeaconState) (*pb.BeaconState, error) {
|
|
|
|
var err error
|
2019-01-05 06:39:07 +00:00
|
|
|
activeValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry, state.Slot)
|
2018-12-29 17:24:11 +00:00
|
|
|
for _, index := range activeValidatorIndices {
|
2019-01-29 04:16:50 +00:00
|
|
|
if state.ValidatorBalances[index] < config.EjectionBalance {
|
2019-01-05 06:39:07 +00:00
|
|
|
state, err = validators.ExitValidator(state, index)
|
2018-12-29 17:24:11 +00:00
|
|
|
if err != nil {
|
2019-01-05 06:39:07 +00:00
|
|
|
return nil, fmt.Errorf("could not exit validator %d: %v", index, err)
|
2018-12-29 17:24:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return state, nil
|
|
|
|
}
|
2018-12-31 18:24:52 +00:00
|
|
|
|
2019-01-24 07:09:52 +00:00
|
|
|
// ProcessPrevSlotShard computes and sets current epoch's calculation slot
|
|
|
|
// and start shard to previous epoch. Then it returns the updated state.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// Set state.previous_epoch_randao_mix = state.current_epoch_randao_mix
|
|
|
|
// Set state.current_epoch_calculation_slot = state.slot
|
|
|
|
func ProcessPrevSlotShard(state *pb.BeaconState) *pb.BeaconState {
|
|
|
|
state.PreviousEpochCalculationSlot = state.CurrentEpochCalculationSlot
|
|
|
|
state.PreviousEpochStartShard = state.CurrentEpochStartShard
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2018-12-31 18:24:52 +00:00
|
|
|
// ProcessValidatorRegistry computes and sets new validator registry fields,
|
2019-01-24 07:09:52 +00:00
|
|
|
// reshuffles shard committees and returns the recomputed state with the updated registry.
|
2018-12-31 18:24:52 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-01-24 07:09:52 +00:00
|
|
|
// Set state.previous_epoch_randao_mix = state.current_epoch_randao_mix
|
|
|
|
// Set state.current_epoch_calculation_slot = state.slot
|
|
|
|
// Set state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committees_per_slot(state) * EPOCH_LENGTH) % SHARD_COUNT
|
|
|
|
// Set state.current_epoch_randao_mix = get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD)
|
2018-12-31 18:24:52 +00:00
|
|
|
func ProcessValidatorRegistry(
|
|
|
|
state *pb.BeaconState) (*pb.BeaconState, error) {
|
2019-01-24 07:09:52 +00:00
|
|
|
state.PreviousEpochRandaoMixHash32 = state.CurrentEpochRandaoMixHash32
|
|
|
|
state.CurrentEpochCalculationSlot = state.Slot
|
2019-01-13 20:52:31 +00:00
|
|
|
|
2019-01-24 07:09:52 +00:00
|
|
|
nextStartShard := (state.CurrentEpochStartShard +
|
|
|
|
validators.CurrCommitteesCountPerSlot(state)*config.EpochLength) %
|
|
|
|
config.EpochLength
|
|
|
|
state.CurrentEpochStartShard = nextStartShard
|
2018-12-31 18:24:52 +00:00
|
|
|
|
2019-01-24 07:09:52 +00:00
|
|
|
var randaoMixSlot uint64
|
|
|
|
if state.CurrentEpochCalculationSlot > config.SeedLookahead {
|
|
|
|
randaoMixSlot = state.CurrentEpochCalculationSlot -
|
|
|
|
config.SeedLookahead
|
2018-12-31 18:24:52 +00:00
|
|
|
}
|
2019-01-24 07:09:52 +00:00
|
|
|
mix, err := randaoMix(state, randaoMixSlot)
|
2018-12-31 18:24:52 +00:00
|
|
|
if err != nil {
|
2019-01-24 07:09:52 +00:00
|
|
|
return nil, fmt.Errorf("could not get randao mix: %v", err)
|
2018-12-31 18:24:52 +00:00
|
|
|
}
|
2019-01-24 07:09:52 +00:00
|
|
|
state.CurrentEpochRandaoMixHash32 = mix
|
2018-12-31 18:24:52 +00:00
|
|
|
|
|
|
|
return state, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessPartialValidatorRegistry processes the portion of validator registry
|
|
|
|
// fields, it doesn't set registry latest change slot. This only gets called if
|
|
|
|
// validator registry update did not happen.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2019-01-24 07:09:52 +00:00
|
|
|
// Set state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot
|
|
|
|
// Set state.previous_epoch_start_shard = state.current_epoch_start_shard
|
|
|
|
// Let epochs_since_last_registry_change = (state.slot - state.validator_registry_latest_change_slot)
|
|
|
|
// EPOCH_LENGTH.
|
|
|
|
// If epochs_since_last_registry_change is an exact power of 2,
|
|
|
|
// set state.current_epoch_calculation_slot = state.slot
|
|
|
|
// set state.current_epoch_randao_mix = state.latest_randao_mixes[
|
|
|
|
// (state.current_epoch_calculation_slot - SEED_LOOKAHEAD) %
|
|
|
|
// LATEST_RANDAO_MIXES_LENGTH].
|
|
|
|
func ProcessPartialValidatorRegistry(state *pb.BeaconState) *pb.BeaconState {
|
|
|
|
epochsSinceLastRegistryChange := (state.Slot - state.ValidatorRegistryUpdateSlot) /
|
|
|
|
config.EpochLength
|
2018-12-31 18:24:52 +00:00
|
|
|
|
|
|
|
if mathutil.IsPowerOf2(epochsSinceLastRegistryChange) {
|
2019-01-24 07:09:52 +00:00
|
|
|
state.CurrentEpochCalculationSlot = state.Slot
|
|
|
|
|
|
|
|
var randaoIndex uint64
|
|
|
|
if state.CurrentEpochCalculationSlot > config.SeedLookahead {
|
|
|
|
randaoIndex = state.CurrentEpochCalculationSlot - config.SeedLookahead
|
2018-12-31 18:24:52 +00:00
|
|
|
}
|
2019-01-24 07:09:52 +00:00
|
|
|
|
|
|
|
randaoMix := state.LatestRandaoMixesHash32S[randaoIndex%config.LatestRandaoMixesLength]
|
|
|
|
state.CurrentEpochRandaoMixHash32 = randaoMix
|
2018-12-31 18:24:52 +00:00
|
|
|
}
|
2019-01-24 07:09:52 +00:00
|
|
|
return state
|
2018-12-31 18:24:52 +00:00
|
|
|
}
|
2019-01-02 20:19:08 +00:00
|
|
|
|
|
|
|
// CleanupAttestations removes any attestation in state's latest attestations
|
|
|
|
// such that the attestation slot is lower than state slot minus epoch length.
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// Remove any attestation in state.latest_attestations such
|
|
|
|
// that attestation.data.slot < state.slot - EPOCH_LENGTH
|
|
|
|
func CleanupAttestations(state *pb.BeaconState) *pb.BeaconState {
|
2019-01-17 01:10:42 +00:00
|
|
|
epochLength := config.EpochLength
|
2019-01-02 20:19:08 +00:00
|
|
|
var earliestSlot uint64
|
|
|
|
|
|
|
|
// If the state slot is less than epochLength, then the earliestSlot would
|
|
|
|
// result in a negative number. Therefore we should default to
|
|
|
|
// earliestSlot = 0 in this case.
|
|
|
|
if state.Slot > epochLength {
|
|
|
|
earliestSlot = state.Slot - epochLength
|
|
|
|
}
|
|
|
|
|
|
|
|
var latestAttestations []*pb.PendingAttestationRecord
|
|
|
|
for _, attestation := range state.LatestAttestations {
|
|
|
|
if attestation.Data.Slot >= earliestSlot {
|
|
|
|
latestAttestations = append(latestAttestations, attestation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state.LatestAttestations = latestAttestations
|
|
|
|
return state
|
|
|
|
}
|
2019-01-17 01:10:42 +00:00
|
|
|
|
|
|
|
// UpdatePenalizedExitBalances ports over the current epoch's penalized exit balances
|
|
|
|
// into next epoch.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// Let e = state.slot // EPOCH_LENGTH.
|
|
|
|
// Set state.latest_penalized_exit_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] =
|
|
|
|
// state.latest_penalized_exit_balances[e % LATEST_PENALIZED_EXIT_LENGTH]
|
|
|
|
func UpdatePenalizedExitBalances(state *pb.BeaconState) *pb.BeaconState {
|
|
|
|
epoch := state.Slot / config.EpochLength
|
|
|
|
nextPenalizedEpoch := (epoch + 1) % config.LatestPenalizedExitLength
|
|
|
|
currPenalizedEpoch := (epoch) % config.LatestPenalizedExitLength
|
2019-01-20 18:26:33 +00:00
|
|
|
state.LatestPenalizedBalances[nextPenalizedEpoch] =
|
|
|
|
state.LatestPenalizedBalances[currPenalizedEpoch]
|
2019-01-17 01:10:42 +00:00
|
|
|
return state
|
|
|
|
}
|