Skip to content

Commit

Permalink
Added Registry updates processing. (erigontech#6820)
Browse files Browse the repository at this point in the history
  • Loading branch information
Giulio2002 authored and finiteops committed Apr 10, 2023
1 parent 8e6c1ee commit 07af929
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 37 deletions.
23 changes: 23 additions & 0 deletions cmd/erigon-cl/core/state/accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,3 +528,26 @@ func (b *BeaconState) BalanceDeltas() (balanceDeltaMap map[uint64]int64, err err
err = b.processInactivityDeltas(balanceDeltaMap, eligibleValidators)
return
}

// Implementation of is_eligible_for_activation_queue. Specs at: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue
func (b *BeaconState) IsValidatorEligibleForActivationQueue(validator *cltypes.Validator) bool {
return validator.ActivationEligibilityEpoch == b.beaconConfig.FarFutureEpoch &&
validator.EffectiveBalance == b.beaconConfig.MaxEffectiveBalance
}

// Implementation of is_eligible_for_activation. Specs at: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_eligible_for_activation
func (b *BeaconState) IsValidatorEligibleForActivation(validator *cltypes.Validator) bool {
return validator.ActivationEligibilityEpoch <= b.finalizedCheckpoint.Epoch &&
validator.ActivationEpoch == b.beaconConfig.FarFutureEpoch
}

// Implementation of get_validator_churn_limit. Specs at: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit
func (b *BeaconState) ValidatorChurnLimit() (limit uint64) {
activeValidatorsCount := uint64(len(b.GetActiveValidatorsIndices(b.Epoch())))
limit = activeValidatorsCount / b.beaconConfig.ChurnLimitQuotient
if limit < b.beaconConfig.MinPerEpochChurnLimit {
limit = b.beaconConfig.MinPerEpochChurnLimit
}
return

}
62 changes: 62 additions & 0 deletions cmd/erigon-cl/core/transition/process_epoch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package transition_test

import (
_ "embed"
"testing"

"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/utils"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/transition"
"github.com/stretchr/testify/require"
)

//go:embed test_data/rewards_penalty_test_expected.ssz_snappy
var expectedRewardsPenaltyState []byte

//go:embed test_data/rewards_penalty_test_state.ssz_snappy
var startingRewardsPenaltyState []byte

//go:embed test_data/registry_updates_test_expected.ssz_snappy
var expectedRegistryUpdatesState []byte

//go:embed test_data/registry_updates_test_state.ssz_snappy
var startingRegistryUpdatesState []byte

func TestProcessRewardsAndPenalties(t *testing.T) {
// Load test states.
testState := state.New(&clparams.MainnetBeaconConfig)
require.NoError(t, utils.DecodeSSZSnappyWithVersion(testState, startingRewardsPenaltyState, int(clparams.BellatrixVersion)))
expected := state.New(&clparams.MainnetBeaconConfig)
require.NoError(t, utils.DecodeSSZSnappyWithVersion(expected, expectedRewardsPenaltyState, int(clparams.BellatrixVersion)))
// Make up state transistor
s := transition.New(testState, &clparams.MainnetBeaconConfig, nil, false)
// Do processing
require.NoError(t, s.ProcessRewardsAndPenalties())
// Now compare if the two states are the same by taking their root and comparing.
haveRoot, err := testState.HashSSZ()
require.NoError(t, err)
expectedRoot, err := testState.HashSSZ()
require.NoError(t, err)
// Lastly compare
require.Equal(t, expectedRoot, haveRoot)
}

func TestProcessRegistryUpdates(t *testing.T) {
// Load test states.
testState := state.New(&clparams.MainnetBeaconConfig)
require.NoError(t, utils.DecodeSSZSnappyWithVersion(testState, startingRegistryUpdatesState, int(clparams.BellatrixVersion)))
expected := state.New(&clparams.MainnetBeaconConfig)
require.NoError(t, utils.DecodeSSZSnappyWithVersion(expected, expectedRegistryUpdatesState, int(clparams.BellatrixVersion)))
// Make up state transistor
s := transition.New(testState, &clparams.MainnetBeaconConfig, nil, false)
// Do processing
require.NoError(t, s.ProcessRegistryUpdates())
// Now compare if the two states are the same by taking their root and comparing.
haveRoot, err := testState.HashSSZ()
require.NoError(t, err)
expectedRoot, err := testState.HashSSZ()
require.NoError(t, err)
// Lastly compare
require.Equal(t, expectedRoot, haveRoot)
}
56 changes: 56 additions & 0 deletions cmd/erigon-cl/core/transition/process_registry_updates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package transition

import "sort"

const preAllocatedSizeActivationQueue = 8192

// computeActivationExitEpoch is Implementation of compute_activation_exit_epoch. Defined in https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_activation_exit_epoch.
func (s *StateTransistor) computeActivationExitEpoch(epoch uint64) uint64 {
return epoch + 1 + s.beaconConfig.MaxSeedLookahead
}

// ProcessRegistyUpdates updates every epoch the activation status of validators. Specs at: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#registry-updates.
func (s *StateTransistor) ProcessRegistryUpdates() error {
currentEpoch := s.state.Epoch()
// start also initializing the activation queue.
activationQueue := make([]uint64, 0, preAllocatedSizeActivationQueue)
validators := s.state.Validators()
// Process activation eligibility and ejections.
for validatorIndex, validator := range validators {
if s.state.IsValidatorEligibleForActivationQueue(validator) {
validator.ActivationEligibilityEpoch = currentEpoch + 1
if err := s.state.SetValidatorAt(validatorIndex, validator); err != nil {
return err
}
}
if validator.Active(currentEpoch) && validator.EffectiveBalance <= s.beaconConfig.EjectionBalance {
if err := s.state.InitiateValidatorExit(uint64(validatorIndex)); err != nil {
return err
}
}
// Insert in the activation queue in case.
if s.state.IsValidatorEligibleForActivation(validator) {
activationQueue = append(activationQueue, uint64(validatorIndex))
}
}
// order the queue accordingly.
sort.Slice(activationQueue, func(i, j int) bool {
// Order by the sequence of activation_eligibility_epoch setting and then index.
if validators[i].ActivationEligibilityEpoch != validators[j].ActivationEligibilityEpoch {
return validators[i].ActivationEligibilityEpoch < validators[j].ActivationEligibilityEpoch
}
return activationQueue[i] < activationQueue[j]
})
// Only process up to epoch limit.
for _, validatorIndex := range activationQueue[:s.state.GetValidatorChurnLimit()] {
validator, err := s.state.ValidatorAt(int(validatorIndex))
if err != nil {
return err
}
validator.ActivationEpoch = s.computeActivationExitEpoch(currentEpoch)
if err := s.state.SetValidatorAt(int(validatorIndex), &validator); err != nil {
return err
}
}
return nil
}

This file was deleted.

Binary file not shown.
Binary file not shown.

0 comments on commit 07af929

Please sign in to comment.