From dd40320a1cd4047efdf1bc0d56d8b4e69f1eb56c Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 4 Mar 2019 23:22:15 -0800 Subject: [PATCH] Save and Delete Validators `Pubkey - Idx` Post Chainstart (#1873) * starting tests * added tests * fixed comments * gaz * make sure key is using EntryExitEffectEpoch --- beacon-chain/blockchain/BUILD.bazel | 2 + beacon-chain/blockchain/service.go | 38 +++++++++++ beacon-chain/blockchain/service_test.go | 81 +++++++++++++++++++++++ beacon-chain/core/validators/validator.go | 12 ++++ 4 files changed, 133 insertions(+) diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 500e8a51d..83fa29896 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", + "//beacon-chain/core/validators:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/powchain:go_default_library", "//proto/beacon/p2p/v1:go_default_library", @@ -36,6 +37,7 @@ go_test( "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", + "//beacon-chain/core/validators:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/internal:go_default_library", "//beacon-chain/powchain:go_default_library", diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index b0624e3f4..cd18fa3d4 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -11,7 +11,9 @@ import ( "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -347,6 +349,14 @@ func (c *ChainService) ReceiveBlock(block *pb.BeaconBlock, beaconState *pb.Beaco "slotsSinceGenesis", beaconState.Slot-params.BeaconConfig().GenesisSlot, ).Info("Block transition successfully processed") if (beaconState.Slot+1)%params.BeaconConfig().SlotsPerEpoch == 0 { + // Save activated validators of this epoch to public key -> index DB. + if err := c.saveValidatorIdx(beaconState); err != nil { + return nil, fmt.Errorf("could not save validator index: %v", err) + } + // Delete exited validators of this epoch to public key -> index DB. + if err := c.deleteValidatorIdx(beaconState); err != nil { + return nil, fmt.Errorf("could not delete validator index: %v", err) + } log.WithField( "SlotsSinceGenesis", beaconState.Slot-params.BeaconConfig().GenesisSlot, ).Info("Epoch transition successfully processed") @@ -380,3 +390,31 @@ func (c *ChainService) isBlockReadyForProcessing(block *pb.BeaconBlock, beaconSt } return nil } + +// saveValidatorIdx saves the validators public key to index mapping in DB, these +// validators were activated from current epoch. After it saves, current epoch key +// is deleted from ActivatedValidators mapping. +func (c *ChainService) saveValidatorIdx(state *pb.BeaconState) error { + for _, idx := range validators.ActivatedValidators[helpers.CurrentEpoch(state)] { + pubKey := state.ValidatorRegistry[idx].Pubkey + if err := c.beaconDB.SaveValidatorIndex(pubKey, int(idx)); err != nil { + return fmt.Errorf("could not save validator index: %v", err) + } + } + delete(validators.ActivatedValidators, helpers.CurrentEpoch(state)) + return nil +} + +// deleteValidatorIdx deletes the validators public key to index mapping in DB, the +// validators were exited from current epoch. After it deletes, current epoch key +// is deleted from ExitedValidators mapping. +func (c *ChainService) deleteValidatorIdx(state *pb.BeaconState) error { + for _, idx := range validators.ExitedValidators[helpers.CurrentEpoch(state)] { + pubKey := state.ValidatorRegistry[idx].Pubkey + if err := c.beaconDB.DeleteValidatorIndex(pubKey); err != nil { + return fmt.Errorf("could not delete validator index: %v", err) + } + } + delete(validators.ExitedValidators, helpers.CurrentEpoch(state)) + return nil +} diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index 355df2c2a..41a73030e 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -16,6 +16,7 @@ import ( b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/internal" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" @@ -709,3 +710,83 @@ func TestIsBlockReadyForProcessing_ValidBlock(t *testing.T) { t.Fatalf("block processing failed despite being a valid block: %v", err) } } + +func TestDeleteValidatorIdx_DeleteWorks(t *testing.T) { + db := internal.SetupDB(t) + defer internal.TeardownDB(t, db) + epoch := uint64(2) + v.ActivatedValidators[epoch] = []uint64{0, 1, 2} + v.ExitedValidators[epoch] = []uint64{0, 2} + var validators []*pb.Validator + for i := 0; i < 3; i++ { + pubKeyBuf := make([]byte, params.BeaconConfig().BLSPubkeyLength) + binary.PutUvarint(pubKeyBuf, uint64(i)) + validators = append(validators, &pb.Validator{ + Pubkey: pubKeyBuf, + }) + } + state := &pb.BeaconState{ + ValidatorRegistry: validators, + Slot: epoch * params.BeaconConfig().SlotsPerEpoch, + } + chainService := setupBeaconChain(t, false, db, true) + if err := chainService.saveValidatorIdx(state); err != nil { + t.Fatalf("Could not save validator idx: %v", err) + } + if err := chainService.deleteValidatorIdx(state); err != nil { + t.Fatalf("Could not delete validator idx: %v", err) + } + wantedIdx := uint64(1) + idx, err := chainService.beaconDB.ValidatorIndex(validators[wantedIdx].Pubkey) + if err != nil { + t.Fatalf("Could not get validator index: %v", err) + } + if wantedIdx != idx { + t.Errorf("Wanted: %d, got: %d", wantedIdx, idx) + } + + wantedIdx = uint64(2) + if chainService.beaconDB.HasValidator(validators[wantedIdx].Pubkey) { + t.Errorf("Validator index %d should have been deleted", wantedIdx) + } + + if _, ok := v.ExitedValidators[epoch]; ok { + t.Errorf("Activated validators mapping for epoch %d still there", epoch) + } +} + +func TestSaveValidatorIdx_SaveRetrieveWorks(t *testing.T) { + db := internal.SetupDB(t) + defer internal.TeardownDB(t, db) + epoch := uint64(1) + v.ActivatedValidators[epoch] = []uint64{0, 1, 2} + var validators []*pb.Validator + for i := 0; i < 3; i++ { + pubKeyBuf := make([]byte, params.BeaconConfig().BLSPubkeyLength) + binary.PutUvarint(pubKeyBuf, uint64(i)) + validators = append(validators, &pb.Validator{ + Pubkey: pubKeyBuf, + }) + } + state := &pb.BeaconState{ + ValidatorRegistry: validators, + Slot: epoch * params.BeaconConfig().SlotsPerEpoch, + } + chainService := setupBeaconChain(t, false, db, true) + if err := chainService.saveValidatorIdx(state); err != nil { + t.Fatalf("Could not save validator idx: %v", err) + } + + wantedIdx := uint64(2) + idx, err := chainService.beaconDB.ValidatorIndex(validators[wantedIdx].Pubkey) + if err != nil { + t.Fatalf("Could not get validator index: %v", err) + } + if wantedIdx != idx { + t.Errorf("Wanted: %d, got: %d", wantedIdx, idx) + } + + if _, ok := v.ActivatedValidators[epoch]; ok { + t.Errorf("Activated validators mapping for epoch %d still there", epoch) + } +} diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 92cd7a88f..3fd46b1fd 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -15,6 +15,15 @@ import ( "github.com/prysmaticlabs/prysm/shared/sliceutil" ) +var ( + // ActivatedValidators is a mapping that tracks epoch to activated validators indexes. + // Where key is epoch, value is a list of activated validator indexes. + ActivatedValidators = make(map[uint64][]uint64) + // ExitedValidators is a mapping that tracks epoch to excited validators indexes. + // Where key is epoch, value is a list of exited validator indexes. + ExitedValidators = make(map[uint64][]uint64) +) + // ValidatorIndices returns all the validator indices from the input attestations // and state. // @@ -279,6 +288,7 @@ func SlashValidator(state *pb.BeaconState, idx uint64) (*pb.BeaconState, error) // state.validator_registry_update_epoch = current_epoch func UpdateRegistry(state *pb.BeaconState) (*pb.BeaconState, error) { currentEpoch := helpers.CurrentEpoch(state) + updatedEpoch := helpers.EntryExitEffectEpoch(currentEpoch) activeValidatorIndices := helpers.ActiveValidatorIndices( state.ValidatorRegistry, currentEpoch) @@ -301,6 +311,7 @@ func UpdateRegistry(state *pb.BeaconState) (*pb.BeaconState, error) { if err != nil { return nil, fmt.Errorf("could not activate validator %d: %v", idx, err) } + ActivatedValidators[updatedEpoch] = append(ActivatedValidators[updatedEpoch], uint64(idx)) } } @@ -314,6 +325,7 @@ func UpdateRegistry(state *pb.BeaconState) (*pb.BeaconState, error) { break } state = ExitValidator(state, uint64(idx)) + ExitedValidators[updatedEpoch] = append(ExitedValidators[updatedEpoch], uint64(idx)) } } state.ValidatorRegistryUpdateEpoch = currentEpoch