Implement Validator Clean Up for Dynasty Transition (#329)

This commit is contained in:
terence tsao
2018-07-26 11:20:55 -07:00
committed by GitHub
parent 8d858aad9b
commit b1c47ab83a
3 changed files with 121 additions and 0 deletions

View File

@@ -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",

View File

@@ -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)

View File

@@ -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))
}
}