mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 16:08:26 -05:00
Implemeting Validator Rewards and Penalties (#323)
* Adding Validator Rewards * Addressing review comments * Adding Penalties * Adding changes * Breaking up functions * Cleaning up * Adding slashing conditions * Adding slashing condition boiler plate * Adding tests * Adding comments * Adding comments * Add in Pubkey * Adding more tests * Adding more unit tests and making name changes * Add beacon chain test helper and fix references to it * Adding rewards test * Adding all tests * Addressing review comments * Remove slashing conditions * fix lint * Fixing merge issues * removing commented function * removing newline * fix golint
This commit is contained in:
@@ -2,6 +2,7 @@ package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
@@ -91,18 +92,26 @@ func (b *BeaconChain) GenesisBlock() (*types.Block, error) {
|
||||
return types.NewGenesisBlock()
|
||||
}
|
||||
|
||||
// isEpochTransition checks if the current slotNumber divided by the epoch length(64 slots)
|
||||
// is greater than the current epoch.
|
||||
func (b *BeaconChain) isEpochTransition(slotNumber uint64) bool {
|
||||
currentEpoch := b.state.CrystallizedState.CurrentEpoch
|
||||
isTransition := (slotNumber / params.EpochLength) > currentEpoch
|
||||
return isTransition
|
||||
}
|
||||
|
||||
// MutateActiveState allows external services to modify the active state.
|
||||
func (b *BeaconChain) MutateActiveState(activeState *types.ActiveState) error {
|
||||
defer b.lock.Unlock()
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.state.ActiveState = activeState
|
||||
return b.persist()
|
||||
}
|
||||
|
||||
// MutateCrystallizedState allows external services to modify the crystallized state.
|
||||
func (b *BeaconChain) MutateCrystallizedState(crystallizedState *types.CrystallizedState) error {
|
||||
defer b.lock.Unlock()
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.state.CrystallizedState = crystallizedState
|
||||
return b.persist()
|
||||
}
|
||||
@@ -262,3 +271,133 @@ func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error)
|
||||
}
|
||||
return indices[:int(attesterCount)], indices[len(indices)-1], nil
|
||||
}
|
||||
|
||||
// hasVoted checks if the attester has voted by looking at the bitfield.
|
||||
func hasVoted(bitfields []byte, attesterBlock int, attesterFieldIndex int) bool {
|
||||
voted := false
|
||||
|
||||
fields := bitfields[attesterBlock-1]
|
||||
attesterField := fields >> (8 - uint(attesterFieldIndex))
|
||||
if attesterField%2 != 0 {
|
||||
voted = true
|
||||
}
|
||||
|
||||
return voted
|
||||
}
|
||||
|
||||
// applyRewardAndPenalty applies the appropriate rewards and penalties according to
|
||||
// whether the attester has voted or not.
|
||||
func (b *BeaconChain) applyRewardAndPenalty(index int, voted bool) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if voted {
|
||||
b.state.CrystallizedState.ActiveValidators[index].Balance += params.AttesterReward
|
||||
} else {
|
||||
// TODO : Change this when penalties are specified for not voting
|
||||
b.state.CrystallizedState.ActiveValidators[index].Balance -= params.AttesterReward
|
||||
}
|
||||
|
||||
return b.persist()
|
||||
}
|
||||
|
||||
// resetAttesterBitfields resets the attester bitfields in the ActiveState to zero.
|
||||
func (b *BeaconChain) resetAttesterBitfields() error {
|
||||
|
||||
length := int(len(b.state.CrystallizedState.ActiveValidators) / 8)
|
||||
if len(b.state.CrystallizedState.ActiveValidators)%8 != 0 {
|
||||
length++
|
||||
}
|
||||
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
newbitfields := make([]byte, length)
|
||||
b.state.ActiveState.AttesterBitfields = newbitfields
|
||||
|
||||
return b.persist()
|
||||
}
|
||||
|
||||
// resetTotalDeposit clears and resets the total attester deposit to zero.
|
||||
func (b *BeaconChain) resetTotalAttesterDeposit() error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
b.state.ActiveState.TotalAttesterDeposits = 0
|
||||
|
||||
return b.persist()
|
||||
}
|
||||
|
||||
// setJustifiedEpoch sets the justified epoch during an epoch transition.
|
||||
func (b *BeaconChain) updateJustifiedEpoch() error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
justifiedEpoch := b.state.CrystallizedState.LastJustifiedEpoch
|
||||
b.state.CrystallizedState.LastJustifiedEpoch = b.state.CrystallizedState.CurrentEpoch
|
||||
|
||||
if b.state.CrystallizedState.CurrentEpoch == (justifiedEpoch + 1) {
|
||||
b.state.CrystallizedState.LastFinalizedEpoch = justifiedEpoch
|
||||
}
|
||||
|
||||
return b.persist()
|
||||
}
|
||||
|
||||
// setRewardsAndPenalties checks if the attester has voted and then applies the
|
||||
// rewards and penalties for them.
|
||||
func (b *BeaconChain) updateRewardsAndPenalties(index int) error {
|
||||
bitfields := b.state.ActiveState.AttesterBitfields
|
||||
attesterBlock := (index + 1) / 8
|
||||
attesterFieldIndex := (index + 1) % 8
|
||||
if attesterFieldIndex == 0 {
|
||||
attesterFieldIndex = 8
|
||||
} else {
|
||||
attesterBlock++
|
||||
}
|
||||
|
||||
if len(bitfields) < attesterBlock {
|
||||
return errors.New("attester index does not exist")
|
||||
}
|
||||
|
||||
voted := hasVoted(bitfields, attesterBlock, attesterFieldIndex)
|
||||
if err := b.applyRewardAndPenalty(index, voted); err != nil {
|
||||
return fmt.Errorf("unable to apply rewards and penalties: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Slashing Condtions
|
||||
// TODO: Implement all the conditions and add in the methods once the spec is updated
|
||||
|
||||
// computeValidatorRewardsAndPenalties is run every epoch transition and appropriates the
|
||||
// rewards and penalties, resets the bitfield and deposits and also applies the slashing conditions.
|
||||
func (b *BeaconChain) computeValidatorRewardsAndPenalties() error {
|
||||
activeValidatorSet := b.state.CrystallizedState.ActiveValidators
|
||||
attesterDeposits := b.state.ActiveState.TotalAttesterDeposits
|
||||
totalDeposit := b.state.CrystallizedState.TotalDeposits
|
||||
|
||||
attesterFactor := attesterDeposits * 3
|
||||
totalFactor := uint64(totalDeposit * 2)
|
||||
|
||||
if attesterFactor >= totalFactor {
|
||||
log.Info("Justified epoch in the crystallised state is set to the current epoch")
|
||||
|
||||
if err := b.updateJustifiedEpoch(); err != nil {
|
||||
return fmt.Errorf("error setting justified epoch: %v", err)
|
||||
}
|
||||
|
||||
for i := range activeValidatorSet {
|
||||
if err := b.updateRewardsAndPenalties(i); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.resetAttesterBitfields(); err != nil {
|
||||
return fmt.Errorf("error resetting bitfields: %v", err)
|
||||
}
|
||||
if err := b.resetTotalAttesterDeposit(); err != nil {
|
||||
return fmt.Errorf("error resetting total deposits: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -31,8 +32,7 @@ func (m *mockFetcher) BlockByHash(ctx context.Context, hash common.Hash) (*gethT
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func TestNewBeaconChain(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
func startInMemoryBeaconChain(t *testing.T) (*BeaconChain, *database.BeaconDB) {
|
||||
config := &database.BeaconDBConfig{DataDir: "", Name: "", InMemory: true}
|
||||
db, err := database.NewBeaconDB(config)
|
||||
if err != nil {
|
||||
@@ -44,6 +44,14 @@ func TestNewBeaconChain(t *testing.T) {
|
||||
t.Fatalf("unable to setup beacon chain: %v", err)
|
||||
}
|
||||
|
||||
return beaconChain, db
|
||||
}
|
||||
|
||||
func TestNewBeaconChain(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
msg := hook.LastEntry().Message
|
||||
want := "No chainstate found on disk, initializing beacon from genesis"
|
||||
if msg != want {
|
||||
@@ -61,16 +69,8 @@ func TestNewBeaconChain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMutateActiveState(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()
|
||||
beaconChain, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to setup beacon chain: %v", err)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
active := &types.ActiveState{
|
||||
TotalAttesterDeposits: 4096,
|
||||
@@ -98,16 +98,8 @@ func TestMutateActiveState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMutateCrystallizedState(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()
|
||||
beaconChain, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to setup beacon chain: %v", err)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
currentCheckpoint := common.BytesToHash([]byte("checkpoint"))
|
||||
crystallized := &types.CrystallizedState{
|
||||
@@ -136,16 +128,8 @@ func TestMutateCrystallizedState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAttestersProposer(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()
|
||||
beaconChain, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to setup beacon chain: %v", err)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
priv, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
@@ -180,16 +164,8 @@ func TestGetAttestersProposer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCanProcessBlock(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()
|
||||
beaconChain, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to setup beacon chain: %v", err)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
block := types.NewBlock(1)
|
||||
// Using a faulty fetcher should throw an error.
|
||||
@@ -234,16 +210,8 @@ func TestCanProcessBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessBlockWithBadHashes(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)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
// Test negative scenario where active state hash is different than node's compute
|
||||
block := types.NewBlock(1)
|
||||
@@ -254,9 +222,9 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
|
||||
}
|
||||
block.InsertActiveHash(stateHash)
|
||||
|
||||
b.state.ActiveState = &types.ActiveState{TotalAttesterDeposits: 9999}
|
||||
beaconChain.state.ActiveState = &types.ActiveState{TotalAttesterDeposits: 9999}
|
||||
|
||||
canProcess, err := b.CanProcessBlock(&mockFetcher{}, block)
|
||||
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err == nil {
|
||||
t.Fatalf("CanProcessBlocks should have failed with diff state hashes")
|
||||
}
|
||||
@@ -272,9 +240,9 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
|
||||
}
|
||||
block.InsertCrystallizedHash(stateHash)
|
||||
|
||||
b.state.CrystallizedState = &types.CrystallizedState{CurrentEpoch: 9999}
|
||||
beaconChain.state.CrystallizedState = &types.CrystallizedState{CurrentEpoch: 9999}
|
||||
|
||||
canProcess, err = b.CanProcessBlock(&mockFetcher{}, block)
|
||||
canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err == nil {
|
||||
t.Fatalf("CanProcessBlocks should have failed with diff state hashes")
|
||||
}
|
||||
@@ -284,16 +252,8 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
activeValidators := []types.ValidatorRecord{
|
||||
{Balance: 10000, WithdrawalAddress: common.Address{'A'}},
|
||||
@@ -319,21 +279,21 @@ func TestRotateValidatorSet(t *testing.T) {
|
||||
{Balance: 99999, WithdrawalAddress: common.Address{'O'}},
|
||||
}
|
||||
|
||||
b.CrystallizedState().ActiveValidators = activeValidators
|
||||
b.CrystallizedState().QueuedValidators = queuedValidators
|
||||
b.CrystallizedState().ExitedValidators = exitedValidators
|
||||
beaconChain.CrystallizedState().ActiveValidators = activeValidators
|
||||
beaconChain.CrystallizedState().QueuedValidators = queuedValidators
|
||||
beaconChain.CrystallizedState().ExitedValidators = exitedValidators
|
||||
|
||||
if b.ActiveValidatorCount() != 5 {
|
||||
t.Errorf("Get active validator count failed, wanted 5, got %v", b.ActiveValidatorCount())
|
||||
if beaconChain.ActiveValidatorCount() != 5 {
|
||||
t.Errorf("Get active validator count failed, wanted 5, got %v", beaconChain.ActiveValidatorCount())
|
||||
}
|
||||
if b.QueuedValidatorCount() != 5 {
|
||||
t.Errorf("Get queued validator count failed, wanted 5, got %v", b.QueuedValidatorCount())
|
||||
if beaconChain.QueuedValidatorCount() != 5 {
|
||||
t.Errorf("Get queued validator count failed, wanted 5, got %v", beaconChain.QueuedValidatorCount())
|
||||
}
|
||||
if b.ExitedValidatorCount() != 5 {
|
||||
t.Errorf("Get exited validator count failed, wanted 5, got %v", b.ExitedValidatorCount())
|
||||
if beaconChain.ExitedValidatorCount() != 5 {
|
||||
t.Errorf("Get exited validator count failed, wanted 5, got %v", beaconChain.ExitedValidatorCount())
|
||||
}
|
||||
|
||||
newQueuedValidators, newActiveValidators, newExitedValidators := b.RotateValidatorSet()
|
||||
newQueuedValidators, newActiveValidators, newExitedValidators := beaconChain.RotateValidatorSet()
|
||||
|
||||
if len(newActiveValidators) != 4 {
|
||||
t.Errorf("Get active validator count failed, wanted 5, got %v", len(newActiveValidators))
|
||||
@@ -345,3 +305,350 @@ func TestRotateValidatorSet(t *testing.T) {
|
||||
t.Errorf("Get exited validator count failed, wanted 6, got %v", len(newExitedValidators))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEpochTransition(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
if err := beaconChain.MutateCrystallizedState(&types.CrystallizedState{CurrentEpoch: 1}); err != nil {
|
||||
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
||||
}
|
||||
if !beaconChain.isEpochTransition(128) {
|
||||
t.Errorf("there was supposed to be an epoch transition but there isn't one now")
|
||||
}
|
||||
if beaconChain.isEpochTransition(80) {
|
||||
t.Errorf("there is not supposed to be an epoch transition but there is one now")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasVoted(t *testing.T) {
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
testfield := int(math.Pow(2, float64(i)))
|
||||
bitfields := []byte{byte(testfield), 0, 0}
|
||||
attesterBlock := 1
|
||||
attesterFieldIndex := (8 - i)
|
||||
|
||||
voted := hasVoted(bitfields, attesterBlock, attesterFieldIndex)
|
||||
if !voted {
|
||||
t.Fatalf("attester was supposed to have voted but the test shows they have not, this is their bitfield and index: %b :%d", bitfields[0], attesterFieldIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRewardAndPenalty(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
priv, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate key: %v", err)
|
||||
}
|
||||
|
||||
balance1 := uint64(10000)
|
||||
balance2 := uint64(15000)
|
||||
balance3 := uint64(20000)
|
||||
balance4 := uint64(25000)
|
||||
balance5 := uint64(30000)
|
||||
|
||||
activeValidators := &types.CrystallizedState{ActiveValidators: []types.ValidatorRecord{
|
||||
{Balance: balance1, WithdrawalAddress: common.Address{'A'}, PubKey: enr.Secp256k1(priv.PublicKey)},
|
||||
{Balance: balance2, WithdrawalAddress: common.Address{'B'}, PubKey: enr.Secp256k1(priv.PublicKey)},
|
||||
{Balance: balance3, WithdrawalAddress: common.Address{'C'}, PubKey: enr.Secp256k1(priv.PublicKey)},
|
||||
{Balance: balance4, WithdrawalAddress: common.Address{'D'}, PubKey: enr.Secp256k1(priv.PublicKey)},
|
||||
{Balance: balance5, WithdrawalAddress: common.Address{'E'}, PubKey: enr.Secp256k1(priv.PublicKey)},
|
||||
}}
|
||||
|
||||
if err := beaconChain.MutateCrystallizedState(activeValidators); err != nil {
|
||||
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
||||
}
|
||||
|
||||
beaconChain.applyRewardAndPenalty(0, true)
|
||||
beaconChain.applyRewardAndPenalty(1, false)
|
||||
beaconChain.applyRewardAndPenalty(2, true)
|
||||
beaconChain.applyRewardAndPenalty(3, false)
|
||||
beaconChain.applyRewardAndPenalty(4, true)
|
||||
|
||||
expectedBalance1 := balance1 + params.AttesterReward
|
||||
expectedBalance2 := balance2 - params.AttesterReward
|
||||
expectedBalance3 := balance3 + params.AttesterReward
|
||||
expectedBalance4 := balance4 - params.AttesterReward
|
||||
expectedBalance5 := balance5 + params.AttesterReward
|
||||
|
||||
if expectedBalance1 != beaconChain.state.CrystallizedState.ActiveValidators[0].Balance {
|
||||
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance1, beaconChain.state.CrystallizedState.ActiveValidators[0].Balance)
|
||||
}
|
||||
|
||||
if expectedBalance2 != beaconChain.state.CrystallizedState.ActiveValidators[1].Balance {
|
||||
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance2, beaconChain.state.CrystallizedState.ActiveValidators[1].Balance)
|
||||
}
|
||||
|
||||
if expectedBalance3 != beaconChain.state.CrystallizedState.ActiveValidators[2].Balance {
|
||||
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance3, beaconChain.state.CrystallizedState.ActiveValidators[2].Balance)
|
||||
}
|
||||
|
||||
if expectedBalance4 != beaconChain.state.CrystallizedState.ActiveValidators[3].Balance {
|
||||
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance4, beaconChain.state.CrystallizedState.ActiveValidators[3].Balance)
|
||||
}
|
||||
|
||||
if expectedBalance5 != beaconChain.state.CrystallizedState.ActiveValidators[4].Balance {
|
||||
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance5, beaconChain.state.CrystallizedState.ActiveValidators[4].Balance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestResetAttesterBitfields(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
priv, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate key: %v", err)
|
||||
}
|
||||
|
||||
// Testing validator set sizes from 1 to 100.
|
||||
|
||||
for j := 1; j <= 100; j++ {
|
||||
|
||||
var validators []types.ValidatorRecord
|
||||
|
||||
for i := 0; i < j; i++ {
|
||||
validator := types.ValidatorRecord{WithdrawalAddress: common.Address{'A'}, PubKey: enr.Secp256k1(priv.PublicKey)}
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
|
||||
if err := beaconChain.MutateCrystallizedState(&types.CrystallizedState{ActiveValidators: validators}); err != nil {
|
||||
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
||||
}
|
||||
|
||||
testAttesterBitfield := []byte{2, 4, 6, 9}
|
||||
|
||||
if err := beaconChain.MutateActiveState(&types.ActiveState{AttesterBitfields: testAttesterBitfield}); err != nil {
|
||||
t.Fatal("unable to mutate active state")
|
||||
}
|
||||
if err := beaconChain.resetAttesterBitfields(); err != nil {
|
||||
t.Fatalf("unable to reset Attester Bitfields")
|
||||
}
|
||||
|
||||
if bytes.Equal(testAttesterBitfield, beaconChain.state.ActiveState.AttesterBitfields) {
|
||||
t.Fatalf("attester bitfields have not been able to be reset: %v", testAttesterBitfield)
|
||||
}
|
||||
|
||||
bitfieldLength := j / 8
|
||||
if j%8 != 0 {
|
||||
bitfieldLength++
|
||||
}
|
||||
|
||||
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfields, make([]byte, bitfieldLength)) {
|
||||
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfields)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestResetTotalAttesterDeposit(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
ActiveState := &types.ActiveState{TotalAttesterDeposits: 10000}
|
||||
if err := beaconChain.MutateActiveState(ActiveState); err != nil {
|
||||
t.Fatalf("unable to Mutate Active state: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(10000) {
|
||||
t.Fatalf("attester deposit was not saved: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
|
||||
}
|
||||
|
||||
if err := beaconChain.resetTotalAttesterDeposit(); err != nil {
|
||||
t.Fatalf("unable to reset total attester deposit: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(0) {
|
||||
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateJustifiedEpoch(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
|
||||
CrystallizedState := &types.CrystallizedState{CurrentEpoch: 5, LastJustifiedEpoch: 4, LastFinalizedEpoch: 3}
|
||||
if err := beaconChain.MutateCrystallizedState(CrystallizedState); err != nil {
|
||||
t.Fatalf("unable to Mutate Active state: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastFinalizedEpoch != uint64(3) ||
|
||||
beaconChain.state.CrystallizedState.LastJustifiedEpoch != uint64(4) ||
|
||||
beaconChain.state.CrystallizedState.CurrentEpoch != uint64(5) {
|
||||
t.Fatal("crystallized state unable to be saved")
|
||||
}
|
||||
|
||||
if err := beaconChain.updateJustifiedEpoch(); err != nil {
|
||||
t.Fatalf("unable to update justified epoch: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastJustifiedEpoch != uint64(5) {
|
||||
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastFinalizedEpoch != uint64(4) {
|
||||
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch)
|
||||
}
|
||||
|
||||
CrystallizedState = &types.CrystallizedState{CurrentEpoch: 8, LastJustifiedEpoch: 4, LastFinalizedEpoch: 3}
|
||||
if err := beaconChain.MutateCrystallizedState(CrystallizedState); err != nil {
|
||||
t.Fatalf("unable to Mutate Active state: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastFinalizedEpoch != uint64(3) ||
|
||||
beaconChain.state.CrystallizedState.LastJustifiedEpoch != uint64(4) ||
|
||||
beaconChain.state.CrystallizedState.CurrentEpoch != uint64(8) {
|
||||
t.Fatal("crystallized state unable to be saved")
|
||||
}
|
||||
|
||||
if err := beaconChain.updateJustifiedEpoch(); err != nil {
|
||||
t.Fatalf("unable to update justified epoch: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastJustifiedEpoch != uint64(8) {
|
||||
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastFinalizedEpoch != uint64(3) {
|
||||
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRewardsAndPenalties(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
priv, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate key: %v", err)
|
||||
}
|
||||
|
||||
var validators []types.ValidatorRecord
|
||||
|
||||
for i := 0; i < 40; i++ {
|
||||
validator := types.ValidatorRecord{Balance: 1000, WithdrawalAddress: common.Address{'A'}, PubKey: enr.Secp256k1(priv.PublicKey)}
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
|
||||
if err := beaconChain.MutateCrystallizedState(&types.CrystallizedState{ActiveValidators: validators}); err != nil {
|
||||
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
||||
}
|
||||
|
||||
//Binary Representation of Bitfield: 00010110 00101011 00101110 01001111 01010000
|
||||
|
||||
testAttesterBitfield := []byte{22, 43, 46, 79, 80}
|
||||
|
||||
if err := beaconChain.MutateActiveState(&types.ActiveState{AttesterBitfields: testAttesterBitfield}); err != nil {
|
||||
t.Fatalf("unable to mutate active state: %v", err)
|
||||
}
|
||||
// Test Validator with index 10 would refer to the 11th bit in the bitfield
|
||||
|
||||
if err := beaconChain.updateRewardsAndPenalties(10); err != nil {
|
||||
t.Fatalf("unable to update rewards and penalties: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[10].Balance != uint64(1001) {
|
||||
t.Fatal("validator balance not updated")
|
||||
}
|
||||
|
||||
// Test Validator with index 15 would refer to the 16th bit in the bitfield
|
||||
if err := beaconChain.updateRewardsAndPenalties(15); err != nil {
|
||||
t.Fatalf("unable to update rewards and penalties: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[15].Balance != uint64(1001) {
|
||||
t.Fatal("validator balance not updated")
|
||||
}
|
||||
|
||||
// Test Validator with index 23 would refer to the 24th bit in the bitfield
|
||||
if err := beaconChain.updateRewardsAndPenalties(23); err != nil {
|
||||
t.Fatalf("unable to update rewards and penalties: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[23].Balance == uint64(1001) {
|
||||
t.Fatal("validator balance not updated")
|
||||
}
|
||||
|
||||
err = beaconChain.updateRewardsAndPenalties(230)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("no error displayed when there is supposed to be one")
|
||||
|
||||
}
|
||||
|
||||
if err.Error() != "attester index does not exist" {
|
||||
t.Fatalf("incorrect error message: ` %s ` is displayed when it should have been: attester index does not exist", err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
|
||||
beaconChain, db := startInMemoryBeaconChain(t)
|
||||
defer db.Stop()
|
||||
priv, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate key: %v", err)
|
||||
}
|
||||
|
||||
var validators []types.ValidatorRecord
|
||||
|
||||
for i := 0; i < 40; i++ {
|
||||
validator := types.ValidatorRecord{Balance: 1000, WithdrawalAddress: common.Address{'A'}, PubKey: enr.Secp256k1(priv.PublicKey)}
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
|
||||
if err := beaconChain.MutateCrystallizedState(&types.CrystallizedState{
|
||||
ActiveValidators: validators,
|
||||
TotalDeposits: 10000,
|
||||
CurrentEpoch: 5,
|
||||
LastJustifiedEpoch: 4,
|
||||
LastFinalizedEpoch: 3}); err != nil {
|
||||
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
||||
}
|
||||
|
||||
//Binary representation of bitfield: 11001000 10010100 10010010 10110011 00110001
|
||||
|
||||
testAttesterBitfield := []byte{200, 148, 146, 179, 49}
|
||||
|
||||
ActiveState := &types.ActiveState{TotalAttesterDeposits: 8000, AttesterBitfields: testAttesterBitfield}
|
||||
if err := beaconChain.MutateActiveState(ActiveState); err != nil {
|
||||
t.Fatalf("unable to Mutate Active state: %v", err)
|
||||
}
|
||||
|
||||
if err := beaconChain.computeValidatorRewardsAndPenalties(); err != nil {
|
||||
t.Fatalf("could not compute validator rewards and penalties: %v", err)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastJustifiedEpoch != uint64(5) {
|
||||
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.LastFinalizedEpoch != uint64(4) {
|
||||
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[0].Balance != uint64(1001) {
|
||||
t.Fatalf("validator balance not updated: %d", beaconChain.state.CrystallizedState.ActiveValidators[1].Balance)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[7].Balance != uint64(999) {
|
||||
t.Fatalf("validator balance not updated: %d", beaconChain.state.CrystallizedState.ActiveValidators[1].Balance)
|
||||
}
|
||||
|
||||
if beaconChain.state.CrystallizedState.ActiveValidators[29].Balance != uint64(999) {
|
||||
t.Fatalf("validator balance not updated: %d", beaconChain.state.CrystallizedState.ActiveValidators[1].Balance)
|
||||
}
|
||||
|
||||
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(0) {
|
||||
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
|
||||
}
|
||||
|
||||
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfields, make([]byte, 5)) {
|
||||
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfields)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (c *ChainService) Start() {
|
||||
log.Errorf("Unable to setup blockchain: %v", err)
|
||||
}
|
||||
c.chain = beaconChain
|
||||
go c.updateActiveState()
|
||||
go c.updateChainState()
|
||||
}
|
||||
|
||||
// Stop the blockchain service's main event loop and associated goroutines.
|
||||
@@ -67,8 +67,10 @@ func (c *ChainService) ContainsBlock(h [32]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// updateActiveState receives a beacon block, computes a new active state and writes it to db.
|
||||
func (c *ChainService) updateActiveState() {
|
||||
// updateChainState receives a beacon block, computes a new active state and writes it to db. Also
|
||||
// it checks for if there is an epoch transition. If there is one it computes the validator rewards
|
||||
// and penalties.
|
||||
func (c *ChainService) updateChainState() {
|
||||
for {
|
||||
select {
|
||||
case block := <-c.latestBeaconBlock:
|
||||
@@ -86,6 +88,15 @@ func (c *ChainService) updateActiveState() {
|
||||
log.Errorf("Write active state to disk failed: %v", err)
|
||||
}
|
||||
|
||||
currentslot := block.SlotNumber()
|
||||
|
||||
transition := c.chain.isEpochTransition(currentslot)
|
||||
if transition {
|
||||
if err := c.chain.computeValidatorRewardsAndPenalties(); err != nil {
|
||||
log.Errorf("Error computing validator rewards and penalties %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
case <-c.ctx.Done():
|
||||
log.Debug("Chain service context closed, exiting goroutine")
|
||||
return
|
||||
|
||||
@@ -5,8 +5,8 @@ const (
|
||||
AttesterCount = 32
|
||||
// AttesterReward determines how much ETH attesters get for performing their duty.
|
||||
AttesterReward = 1
|
||||
// EpochLength is the beacon chain epoch length in blocks.
|
||||
EpochLength = 5
|
||||
// EpochLength is the beacon chain epoch length in slots.
|
||||
EpochLength = 64
|
||||
// ShardCount is a fixed number.
|
||||
ShardCount = 20
|
||||
// DefaultBalance of a validator.
|
||||
|
||||
Reference in New Issue
Block a user