mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 16:08:26 -05:00
Implement Validator Clean Up for Dynasty Transition (#329)
This commit is contained in:
@@ -31,6 +31,7 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/database:go_default_library",
|
||||
"//beacon-chain/params:go_default_library",
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
"//beacon-chain/types:go_default_library",
|
||||
"//beacon-chain/utils:go_default_library",
|
||||
|
||||
@@ -74,6 +74,21 @@ func (b *BeaconChain) CrystallizedState() *types.CrystallizedState {
|
||||
return b.state.CrystallizedState
|
||||
}
|
||||
|
||||
// ActiveValidatorCount exposes a getter to total number of active validator.
|
||||
func (b *BeaconChain) ActiveValidatorCount() int {
|
||||
return len(b.state.CrystallizedState.ActiveValidators)
|
||||
}
|
||||
|
||||
// QueuedValidatorCount exposes a getter to total number of queued validator.
|
||||
func (b *BeaconChain) QueuedValidatorCount() int {
|
||||
return len(b.state.CrystallizedState.QueuedValidators)
|
||||
}
|
||||
|
||||
// ExitedValidatorCount exposes a getter to total number of exited validator.
|
||||
func (b *BeaconChain) ExitedValidatorCount() int {
|
||||
return len(b.state.CrystallizedState.ExitedValidators)
|
||||
}
|
||||
|
||||
// GenesisBlock returns the canonical, genesis block.
|
||||
func (b *BeaconChain) GenesisBlock() *types.Block {
|
||||
return types.NewGenesisBlock()
|
||||
@@ -131,6 +146,47 @@ func (b *BeaconChain) CanProcessBlock(fetcher powchain.POWBlockFetcher, block *t
|
||||
return validTime, nil
|
||||
}
|
||||
|
||||
// RotateValidatorSet is called every dynasty transition. It's primary function is
|
||||
// to go through queued validators and induct them to be active, and remove bad
|
||||
// active validator whose balance is below threshold to the exit set. It also cross checks
|
||||
// every validator's switch dynasty before induct or remove.
|
||||
func (b *BeaconChain) RotateValidatorSet() ([]types.ValidatorRecord, []types.ValidatorRecord, []types.ValidatorRecord) {
|
||||
|
||||
var newExitedValidators = b.CrystallizedState().ExitedValidators
|
||||
var newActiveValidators []types.ValidatorRecord
|
||||
upperbound := b.ActiveValidatorCount()/30 + 1
|
||||
exitCount := 0
|
||||
|
||||
// Loop through active validator set, remove validator whose balance is below 50% and switch dynasty > current dynasty.
|
||||
for _, validator := range b.state.CrystallizedState.ActiveValidators {
|
||||
if validator.Balance < params.DefaultBalance/2 {
|
||||
newExitedValidators = append(newExitedValidators, validator)
|
||||
} else if validator.SwitchDynasty == b.CrystallizedState().Dynasty+1 && exitCount < upperbound {
|
||||
newExitedValidators = append(newExitedValidators, validator)
|
||||
exitCount++
|
||||
} else {
|
||||
newActiveValidators = append(newActiveValidators, validator)
|
||||
}
|
||||
}
|
||||
// Get the total number of validator we can induct.
|
||||
inductNum := upperbound
|
||||
if b.QueuedValidatorCount() < inductNum {
|
||||
inductNum = b.QueuedValidatorCount()
|
||||
}
|
||||
|
||||
// Induct queued validator to active validator set until the switch dynasty is greater than current number.
|
||||
for i := 0; i < inductNum; i++ {
|
||||
if b.CrystallizedState().QueuedValidators[i].SwitchDynasty > b.CrystallizedState().Dynasty+1 {
|
||||
inductNum = i
|
||||
break
|
||||
}
|
||||
newActiveValidators = append(newActiveValidators, b.CrystallizedState().QueuedValidators[i])
|
||||
}
|
||||
newQueuedValidators := b.CrystallizedState().QueuedValidators[inductNum:]
|
||||
|
||||
return newQueuedValidators, newActiveValidators, newExitedValidators
|
||||
}
|
||||
|
||||
// persist stores the RLP encoding of the latest beacon chain state into the db.
|
||||
func (b *BeaconChain) persist() error {
|
||||
encodedState, err := rlp.EncodeToBytes(b.state)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/database"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -281,3 +282,66 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
|
||||
t.Errorf("CanProcessBlocks should have returned false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRotateValidatorSet(t *testing.T) {
|
||||
config := &database.BeaconDBConfig{DataDir: "", Name: "", InMemory: true}
|
||||
db, err := database.NewBeaconDB(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to setup db: %v", err)
|
||||
}
|
||||
db.Start()
|
||||
b, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to setup beacon chain: %v", err)
|
||||
}
|
||||
|
||||
activeValidators := []types.ValidatorRecord{
|
||||
{Balance: 10000, WithdrawalAddress: common.Address{'A'}},
|
||||
{Balance: 15000, WithdrawalAddress: common.Address{'B'}},
|
||||
{Balance: 20000, WithdrawalAddress: common.Address{'C'}},
|
||||
{Balance: 25000, WithdrawalAddress: common.Address{'D'}},
|
||||
{Balance: 30000, WithdrawalAddress: common.Address{'E'}},
|
||||
}
|
||||
|
||||
queuedValidators := []types.ValidatorRecord{
|
||||
{Balance: params.DefaultBalance, WithdrawalAddress: common.Address{'F'}},
|
||||
{Balance: params.DefaultBalance, WithdrawalAddress: common.Address{'G'}},
|
||||
{Balance: params.DefaultBalance, WithdrawalAddress: common.Address{'H'}},
|
||||
{Balance: params.DefaultBalance, WithdrawalAddress: common.Address{'I'}},
|
||||
{Balance: params.DefaultBalance, WithdrawalAddress: common.Address{'J'}},
|
||||
}
|
||||
|
||||
exitedValidators := []types.ValidatorRecord{
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'K'}},
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'L'}},
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'M'}},
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'N'}},
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'O'}},
|
||||
}
|
||||
|
||||
b.CrystallizedState().ActiveValidators = activeValidators
|
||||
b.CrystallizedState().QueuedValidators = queuedValidators
|
||||
b.CrystallizedState().ExitedValidators = exitedValidators
|
||||
|
||||
if b.ActiveValidatorCount() != 5 {
|
||||
t.Errorf("Get active validator count failed, wanted 5, got %v", b.ActiveValidatorCount())
|
||||
}
|
||||
if b.QueuedValidatorCount() != 5 {
|
||||
t.Errorf("Get queued validator count failed, wanted 5, got %v", b.QueuedValidatorCount())
|
||||
}
|
||||
if b.ExitedValidatorCount() != 5 {
|
||||
t.Errorf("Get exited validator count failed, wanted 5, got %v", b.ExitedValidatorCount())
|
||||
}
|
||||
|
||||
newQueuedValidators, newActiveValidators, newExitedValidators := b.RotateValidatorSet()
|
||||
|
||||
if len(newActiveValidators) != 4 {
|
||||
t.Errorf("Get active validator count failed, wanted 5, got %v", len(newActiveValidators))
|
||||
}
|
||||
if len(newQueuedValidators) != 4 {
|
||||
t.Errorf("Get queued validator count failed, wanted 4, got %v", len(newQueuedValidators))
|
||||
}
|
||||
if len(newExitedValidators) != 7 {
|
||||
t.Errorf("Get exited validator count failed, wanted 6, got %v", len(newExitedValidators))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user