prysm-pulse/beacon-chain/rpc/beacon/validators_test.go
terence tsao 89eedd2123 Efficient computation of epoch participation (#4430)
* Remove custody (#3986)

* Update proto fields

* Updated block operations

* Fixed all block operation tests

* Fixed tests part 1

* Fixed tests part 1

* All tests pass

* Clean up

* Skip spec test

* Fixed ssz test

* Skip ssz test

* Skip mainnet tests

* Update beacon-chain/operations/attestation.go

* Update beacon-chain/operations/attestation.go
* Decoy flip flop check (#3987)
* Bounce attack check (#3989)

* New store values

* Update process block

* Update process attestation

* Update tests

* Helper

* Fixed blockchain package tests

* Update beacon-chain/blockchain/forkchoice/process_block.go
* Conflict
* Unskip mainnet spec tests (#3998)

* Starting

* Fixed attestation mainnet test

* Unskip ssz static and block processing tests

* Fixed workspace

* fixed workspace

* fixed workspace

* Update beacon-chain/core/blocks/block_operations.go
* Unskip minimal spec tests (#3999)

* Starting

* Fixed attestation mainnet test

* Unskip ssz static and block processing tests

* Fixed workspace

* fixed workspace

* fixed workspace

* Update workspace

* Unskip all minimal spec tests

* Update workspace for general test
* Unskip test (#4001)
* Update minimal seconds per slot to 6 (#3978)
* Bounce attack tests (#3993)

* New store values

* Update process block

* Update process attestation

* Update tests

* Helper

* Fixed blockchain package tests

* Slots since epoch starts tests

* Update justified checkpt tests

* Conflict

* Fixed logic

* Update process_block.go

* Use helper
* Conflict
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.1
* Conflict
* Fixed failed tests
* Lower MinGenesisActiveValidatorCount to 16384 (#4100)
* Fork choice beacon block checks (#4107)

* Prevent future blocks check and test

* Removed old code
* Update aggregation proto (#4121)

* Update def
* Update spec test
* Conflict
* Update workspace
* patch
* Resolve conflict
* Patch
* Change workspace
* Update ethereumapis to a forked branch at commit 6eb1193e47
* Fixed all the tests
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into conflict
* fix patch
* Need to regenerate test data
* Merge branch 'master' into v0.9.2
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Enable snappy compression for all (#4157)

* enable snappy compression for all
* enable snappy compression for all
* enable snappy compression for all
* enable snappy compression for all
* Validate aggregate and proof subscriber (#4159)
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Conflict
* Update workspace
* Conflict
* Conflict
* Conflict
* Merge branch 'master' into v0.9.2
* Merge branch 'master' into v0.9.2
* Conflict
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Remove migrate to snappy  (#4205)
* Feature flag: Deprecate --prune-states, release to all (#4204)

* Deprecated prune-states, release to all

* imports

* remote unused import

* remove unused import

* Rm prune state test

* gaz
* Refactoring for dynamic pubsub subscriptions for non-aggregated attestations (#4189)

* checkpoint progress

* chkpt

* checkpoint progress

* put pipeline in its own file

* remove unused imports

* add test, it's failing though

* fix test

* remove head state issue

* add clear db flag to e2e

* add some more error handling, debug logging

* skip processing if chain has not started

* fix test

* wrap in go routine to see if anything breaks

* remove duplicated topic

* Add a regression test. Thanks @nisdas for finding the original problem. May it never happen again *fingers crossed*

* Comments

* gofmt

* comment out with TODO
* Sync with master
* Sync with master
* RPC servers use attestation pool (#4223)
* Merge branch 'master' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Refactor RPC to Fully Utilize Ethereum APIs (#4243)

* include attester as a file in the validator server

* remove old proposer server impl

* include new patch and properly sync changes

* align with public pbs

* ensure matches rpc def

* fix up status tests

* resolve all broken test files in the validator rpc package

* gazelle include

* fix up the duties implementation

* fixed up all get duties functions

* all tests pass

* utilize new ethereum apis

* amend validator client to use the new beacon node validator rpc client

* fix up most of validator items

* added in mock

* fix up test

* readd test

* add chain serv mock

* fix a few more validator methods

* all validator tests passingggg

* fix broken test

* resolve even more broken tests

* all tests passsssss

* fix lint

* try PR

* fix up test

* resolve broken other tests
* Sync with master
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into v0.9.2
* Aggregate and proof subscriber (#4240)

* Added subscribers

* Fixed conflict

* Tests

* fix up patch

* Use upstream pb

* include latest patch

* Fmt

* Save state before head block
* skip tests (#4275)
* Delete block attestations from the pool (#4241)

* Added subscribers
* Clean up
* Fixed conflict
* Delete atts in pool in validate pipeline
* Moved it to subscriber
* Merge branch 'v0.9.2' of https://github.com/prysmaticlabs/prysm into use-att-pool-3
* Test
* Fixed test
* Initial work on voluntary exit (#4207)

* Initial implementation of voluntary exit: RPC call

* Update for recent merges

* Break out validation logic for voluntary exits to core module

* RequestExit -> ProposeExit

* Decrease exit package visibility

* Move to operation feed

* Wrap errors
* Fix critical proposer selection bug #4259 (#4265)

* fix critical proposer selection bug #4259

* gofmt

* add 1 more validator to make it 5

* more tests

* Fixed archivedProposerIndex

* Fixed TestFilterAttestation_OK

* Refactor ComputeProposerIndex, add regression test for potential out of range panic

* handle case of nil validator

* Update validators_test.go
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Leftover merge files, oops
* gaz
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into v0.9.2
* Fixes Duplicate Validator Bug (#4322)

* Update dict

* Test helper

* Regression test

* Comment

* Reset test cache
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* fixes after PR #4328
* Complete attestation pool for run time (#4286)

* Added subscribers

* Fixed conflict

* Delete atts in pool in validate pipeline

* Moved it to subscriber

* Test

* Fixed test

* New curl for forkchoice attestations

* Starting att pool service for fork choice

* Update pool interface

* Update pool interface

* Update sync and node

* Lint

* Gazelle

* Updated servers, filled in missing functionalities

* RPC working with 1 beacon node 64 validators

* Started writing tests. Yay

* Test to aggregate and save multiple fork choice atts

* Tests for BatchAttestations for fork choice

* Fixed exisiting tests

* Minor fixes

* Fmt

* Added batch saves

* Lint

* Mo tests yay

* Delete test

* Fmt

* Update interval

* Fixed aggregation broadcast

* Clean up based on design review comment

* Fixed setupBeaconChain

* Raul's feedback. s/error/err
* resolve conflicts
* Merge branch 'v0.9.2' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* Removed old protos and fixed tests (#4336)
* Merge refs/heads/master into v0.9.2
* Disallow duplicated indices and test (#4339)
* Explicit use of GENESIS_SLOT in fork choice (#4343)
* Update from 2 to 3 (#4345)
* Remove verify unaggregated attestation when aggregating (#4347)
* use slot ticker instead of run every (#4348)
* Add context check for unbounded loop work (#4346)
* Revert "Explicit use of GENESIS_SLOT in fork choice (#4343)" (#4349)

This reverts commit d3f6753c77f8f733563d00ab649c5159b2c2926f.
* Refactor Powchain Service (#4306)

* add data structures

* generate proto

* add in new fields

* add comments

* add new mock state

* add new mock state

* add new methods

* some more changes

* check genesis time properly

* lint

* fix refs

* fix tests

* lint

* lint

* lint

* gaz

* fix lint

* raul's comments

* use one method

* fix test

* raul's comment

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Ensure best better-justification is stored for fork choice (#4342)

* Ensure best better-justification is stored. Minor refactor
* Tests
* Merge refs/heads/v0.9.2 into better-best-justified
* Merge refs/heads/v0.9.2 into better-best-justified
* Ensure that epoch of attestation slot matches the target epoch (#4341)

* Disallow duplicated indices and test
* Add slot to target epoch check to on_attestation
* Add slot to target epoch check to process_attestation
* Merge branch 'v0.9.2' of git+ssh://github.com/prysmaticlabs/prysm into no-dup-att-indices
* Fixed TestProcessAttestations_PrevEpochFFGDataMismatches
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Update beacon-chain/blockchain/forkchoice/process_attestation_test.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>
* Merge refs/heads/v0.9.2 into no-dup-att-indices
* Filter viable branches in fork choice (#4355)
* Only activate upon finality (#4359)

* Updated functions
* Tests
* Merge branch 'v0.9.2' of git+ssh://github.com/prysmaticlabs/prysm into queue-fix-on-finality
* Comment
* Merge refs/heads/v0.9.2 into queue-fix-on-finality
* Fixed failing test from 4359 (#4360)

* Fixed
* Skip registry spec tests
* Wait for state to be initialized at least once before running slot ticker based on genesis time (#4364)
* Sync with master
* Fix checkpoint root to  use genesis block root (#4368)
* Return an error on nil head state in fork choice (#4369)

* Return error if nil head state

* Fixed tests. Saved childen blocks state

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
* Update metrics every epoch (#4367)
* return empty slice if state is nil (#4365)
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* Pubsub: Broadcast attestations to committee based subnets (#4316)

* Working on un-aggregated pubsub topics

* update subscriber to call pool

* checkpointing

* fix

* untested message validation

* minor fixes

* rename slotsSinceGenesis to slotsSince

* some progress on a unit test, subscribe is not being called still...

* dont change topic

* need to set the data on the message

* restore topic

* fixes

* some helpful parameter changes for mainnet operations

* lint

* Terence feedback

* unskip e2e

* Unit test for validate committee index beacon attestation

* PR feedbacK

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into resolveConflicts
* remove condition
* Remove unused operation pool (#4361)
* Merge refs/heads/master into v0.9.2
* Aggregate attestations periodically  (#4376)
* Persist ETH1 Data to Disk (#4329)

* add data structures

* generate proto

* add in new fields

* add comments

* add new mock state

* add new mock state

* add new methods

* some more changes

* check genesis time properly

* lint

* fix refs

* fix tests

* lint

* lint

* lint

* gaz

* adding in new proto message

* remove outdated vars

* add new changes

* remove latest eth1data

* continue refactoring

* finally works

* lint

* fix test

* fix all tests

* fix all tests again

* fix build

* change back

* add full eth1 test

* fix logs and test

* add constant

* changes

* fix bug

* lint

* fix another bug

* change back

* Apply suggestions from code review

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
* Fixed VerifyIndexedAttestation (#4382)
* rm signing root (#4381)

* rm signing root

* Fixed VerifyIndexedAttestation

* Check proposer slashed status inside ProcessBlockHeaderNoVerify

* Fixed TestUpdateJustified_CouldUpdateBest

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Remove Redundant Trie Generation (#4383)

* remove trie generation
* remove deposit hashes
* Merge branch 'master' of https://github.com/prysmaticlabs/geth-sharding into v0.9.2
* fix build
* Conflict
* Implement StreamAttestations RPC Endpoint (#4390)

* started attestation stream

* stream attestations test

* on slot tick test passing

* imports

* gaz

* Update beacon-chain/rpc/beacon/attestations_test.go

Co-Authored-By: shayzluf <thezluf@gmail.com>

Co-authored-by: shayzluf <thezluf@gmail.com>
* Fixed goimport (#4394)
* Use custom stateutil ssz for ssz HTR spec tests (#4396)

* Use custom stateutil ssz for ssz HTR spec tests

* gofmt
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge refs/heads/master into v0.9.2
* set mainnet to be the default for build and run (#4398)

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* gracefully handle deduplicated registration of topic validators (#4399)

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* SSZ: temporarily disable roots cache until cache issues can be resolved (#4407)

* temporarily disable roots cache until cache issues can be resolved

* Also use custom ssz for spectests

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Remove process block attestations as separate routine (#4408)

* Removed old save/process block atts

* Fixed tests

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Save Deposit Cache to Disk (#4384)

* change to protos

* fix build

* glue everything together

* fix test

* raul's review

* preston's comments

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Fix activation queue sorting (#4409)

* Removed old save/process block atts

* Fixed tests

* Proper sorting by eligibility epoch then by indices

* Deleted old colde
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Merge branch 'master' into v0.9.2
* Merge refs/heads/master into v0.9.2
* stop recursive lookup if context is cancelled (#4420)
* Fix proposal bug (#4419)
* Add Pending Deposits Safely (#4422)

* safely prune cache

* use proper method

* preston's,terence's reviews and comments

* revert change to build files

* use as feature config instead
* Release custom state ssz (#4421)

* Release custom state ssz, change all HTR of beacon state to use custom method

* typo

* use mainnet config

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Merge branch 'master' of github.com:prysmaticlabs/prysm into v0.9.2
* Define framework
* Use participation fetcher
* Build
* Fixed all tests
* Lint
* Update initial sync save justified to align with v0.9.3 (#4432)
* Merge refs/heads/master into v0.9.2
* Merge refs/heads/master into v0.9.2
* fix build
* don't blacklist on pubsub (#4435)
* Fix Flakey Slot Ticker Test (#4434)

* use interface instead for the slot ticker

* fixed up flakey tests

* add gen time

* get duties comment

* fix lifecycle test

* more fixes
* Fixed rest of the test
* Pass in correct chain service
* Pass in another chain service
* Run time
* Configurable min genesis delay (#4437)

* Configurable min genesis delay based on https://github.com/ethereum/eth2.0-specs/pull/1557

* remove feature flag for genesis delay

* fix

* demo config feedback
* Current -> Prev
* Tests
* patch readme
* save keys unencrypted for validators (#4439)
* Add new demo configuration targeting mainnet scale (#4397)

* Add new demo configuration targeting mainnet, with 1/10th of the deposit value

* reduce quotant by 1/10th. Use 1/10th mainnet values

* only change the inactivity quotant

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Save justified checkpoint state (#4433)

* Save justified checkpoint state

* Lint

* Feedback

* Fixed test

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
* Update shared/testutil/deposits.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update proto/testing/ssz_regression_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/core/epoch/epoch_processing.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/kv/forkchoice.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/service.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber_beacon_blocks_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber_beacon_blocks_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/sync/subscriber.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/proposer.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/prepare_forkchoice.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/operations/attestations/pool.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/powchain/log_processing_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/aggregator/server.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/rpc/validator/exit_test.go

Co-Authored-By: Ivan Martinez <ivanthegreatdev@gmail.com>
* Update beacon-chain/cache/depositcache/pending_deposits.go
* Update beacon-chain/cache/depositcache/pending_deposits_test.go
* Update beacon-chain/rpc/validator/proposer.go
* Merge refs/heads/master into v0.9.2
* Update test
* Conflict
* Update beacon-chain/blockchain/chain_info.go
* Conflict
* Merge branch 'efficient-participation' of git+ssh://github.com/prysmaticlabs/prysm into efficient-participation
* Merge refs/heads/master into efficient-participation
2020-01-07 19:28:25 +00:00

1521 lines
41 KiB
Go

package beacon
import (
"context"
"fmt"
"reflect"
"strconv"
"strings"
"testing"
"github.com/gogo/protobuf/proto"
ptypes "github.com/gogo/protobuf/types"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func init() {
// Use minimal config to reduce test setup time.
params.OverrideBeaconConfig(params.MinimalSpecConfig())
}
func TestServer_ListValidatorBalances_CannotRequestFutureEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := "Cannot retrieve information about an epoch in the future"
if _, err := bs.ListValidatorBalances(
ctx,
&ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{
Epoch: 1,
},
},
); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidatorBalances_NoResults(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := &ethpb.ValidatorBalances{
Balances: make([]*ethpb.ValidatorBalances_Balance, 0),
TotalSize: int32(0),
NextPageToken: strconv.Itoa(0),
}
res, err := bs.ListValidatorBalances(
ctx,
&ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{
Epoch: 0,
},
},
)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
}
func TestServer_ListValidatorBalances_DefaultResponse_NoArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
numItems := 100
validators := make([]*ethpb.Validator, numItems)
balances := make([]uint64, numItems)
balancesResponse := make([]*ethpb.ValidatorBalances_Balance, numItems)
for i := 0; i < numItems; i++ {
validators[i] = &ethpb.Validator{
PublicKey: []byte(strconv.Itoa(i)),
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
balancesResponse[i] = &ethpb.ValidatorBalances_Balance{
PublicKey: []byte(strconv.Itoa(i)),
Index: uint64(i),
Balance: params.BeaconConfig().MaxEffectiveBalance,
}
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: 0,
Validators: validators,
Balances: balances,
},
},
}
res, err := bs.ListValidatorBalances(
ctx,
&ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{
Epoch: 0,
},
},
)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(balancesResponse, res.Balances) {
t.Errorf("Wanted %v, received %v", balancesResponse, res.Balances)
}
}
func TestServer_ListValidatorBalances_DefaultResponse_FromArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
currentNumValidators := 100
numOldBalances := 50
validators := make([]*ethpb.Validator, currentNumValidators)
balances := make([]uint64, currentNumValidators)
oldBalances := make([]uint64, numOldBalances)
balancesResponse := make([]*ethpb.ValidatorBalances_Balance, numOldBalances)
for i := 0; i < currentNumValidators; i++ {
validators[i] = &ethpb.Validator{
PublicKey: []byte(strconv.Itoa(i)),
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
for i := 0; i < numOldBalances; i++ {
oldBalances[i] = params.BeaconConfig().MaxEffectiveBalance
balancesResponse[i] = &ethpb.ValidatorBalances_Balance{
PublicKey: []byte(strconv.Itoa(i)),
Index: uint64(i),
Balance: params.BeaconConfig().MaxEffectiveBalance,
}
}
// We archive old balances for epoch 50.
if err := db.SaveArchivedBalances(ctx, 50, oldBalances); err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: helpers.StartSlot(100 /* epoch 100 */),
Validators: validators,
Balances: balances,
},
},
}
res, err := bs.ListValidatorBalances(
ctx,
&ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{
Epoch: 50,
},
},
)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(balancesResponse, res.Balances) {
t.Errorf("Wanted %v, received %v", balancesResponse, res.Balances)
}
}
func TestServer_ListValidatorBalances_PaginationOutOfRange(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
setupValidators(t, db, 3)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
req := &ethpb.ListValidatorBalancesRequest{PageToken: strconv.Itoa(1), PageSize: 100}
wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(headState.Balances))
if _, err := bs.ListValidatorBalances(context.Background(), req); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidatorBalances_ExceedsMaxPageSize(t *testing.T) {
bs := &Server{}
exceedsMax := int32(params.BeaconConfig().MaxPageSize + 1)
wanted := fmt.Sprintf(
"Requested page size %d can not be greater than max size %d",
exceedsMax,
params.BeaconConfig().MaxPageSize,
)
req := &ethpb.ListValidatorBalancesRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax}
if _, err := bs.ListValidatorBalances(context.Background(), req); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidatorBalances_Pagination_Default(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
setupValidators(t, db, 100)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{State: headState},
}
tests := []struct {
req *ethpb.ListValidatorBalancesRequest
res *ethpb.ValidatorBalances
}{
{req: &ethpb.ListValidatorBalancesRequest{PublicKeys: [][]byte{{99}}},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{Index: 99, PublicKey: []byte{99}, Balance: 99},
},
NextPageToken: "",
TotalSize: 1,
},
},
{req: &ethpb.ListValidatorBalancesRequest{Indices: []uint64{1, 2, 3}},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{Index: 1, PublicKey: []byte{1}, Balance: 1},
{Index: 2, PublicKey: []byte{2}, Balance: 2},
{Index: 3, PublicKey: []byte{3}, Balance: 3},
},
NextPageToken: "",
TotalSize: 3,
},
},
{req: &ethpb.ListValidatorBalancesRequest{PublicKeys: [][]byte{{10}, {11}, {12}}},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{Index: 10, PublicKey: []byte{10}, Balance: 10},
{Index: 11, PublicKey: []byte{11}, Balance: 11},
{Index: 12, PublicKey: []byte{12}, Balance: 12},
},
NextPageToken: "",
TotalSize: 3,
}},
{req: &ethpb.ListValidatorBalancesRequest{PublicKeys: [][]byte{{2}, {3}}, Indices: []uint64{3, 4}}, // Duplication
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{Index: 2, PublicKey: []byte{2}, Balance: 2},
{Index: 3, PublicKey: []byte{3}, Balance: 3},
{Index: 4, PublicKey: []byte{4}, Balance: 4},
},
NextPageToken: "",
TotalSize: 3,
}},
{req: &ethpb.ListValidatorBalancesRequest{PublicKeys: [][]byte{{}}, Indices: []uint64{3, 4}}, // Public key has a blank value
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{Index: 3, PublicKey: []byte{3}, Balance: 3},
{Index: 4, PublicKey: []byte{4}, Balance: 4},
},
NextPageToken: "",
TotalSize: 2,
}},
}
for _, test := range tests {
res, err := bs.ListValidatorBalances(context.Background(), test.req)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(res, test.res) {
t.Errorf("Expected %v, received %v", test.res, res)
}
}
}
func TestServer_ListValidatorBalances_Pagination_CustomPageSizes(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
count := 1000
setupValidators(t, db, count)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
tests := []struct {
req *ethpb.ListValidatorBalancesRequest
res *ethpb.ValidatorBalances
}{
{req: &ethpb.ListValidatorBalancesRequest{PageToken: strconv.Itoa(1), PageSize: 3},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{PublicKey: []byte{3}, Index: 3, Balance: uint64(3)},
{PublicKey: []byte{4}, Index: 4, Balance: uint64(4)},
{PublicKey: []byte{5}, Index: 5, Balance: uint64(5)}},
NextPageToken: strconv.Itoa(2),
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorBalancesRequest{PageToken: strconv.Itoa(10), PageSize: 5},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{PublicKey: []byte{50}, Index: 50, Balance: uint64(50)},
{PublicKey: []byte{51}, Index: 51, Balance: uint64(51)},
{PublicKey: []byte{52}, Index: 52, Balance: uint64(52)},
{PublicKey: []byte{53}, Index: 53, Balance: uint64(53)},
{PublicKey: []byte{54}, Index: 54, Balance: uint64(54)}},
NextPageToken: strconv.Itoa(11),
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorBalancesRequest{PageToken: strconv.Itoa(33), PageSize: 3},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{PublicKey: []byte{99}, Index: 99, Balance: uint64(99)},
{PublicKey: []byte{100}, Index: 100, Balance: uint64(100)},
{PublicKey: []byte{101}, Index: 101, Balance: uint64(101)},
},
NextPageToken: "34",
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorBalancesRequest{PageSize: 2},
res: &ethpb.ValidatorBalances{
Balances: []*ethpb.ValidatorBalances_Balance{
{PublicKey: []byte{0}, Index: 0, Balance: uint64(0)},
{PublicKey: []byte{1}, Index: 1, Balance: uint64(1)}},
NextPageToken: strconv.Itoa(1),
TotalSize: int32(count)}},
}
for _, test := range tests {
res, err := bs.ListValidatorBalances(context.Background(), test.req)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(res, test.res) {
t.Errorf("Expected %v, received %v", test.res, res)
}
}
}
func TestServer_ListValidatorBalances_OutOfRange(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
setupValidators(t, db, 1)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{State: headState},
}
req := &ethpb.ListValidatorBalancesRequest{Indices: []uint64{uint64(1)}}
wanted := "does not exist"
if _, err := bs.ListValidatorBalances(context.Background(), req); !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidatorBalances_FromArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
epoch := uint64(0)
validators, balances := setupValidators(t, db, 100)
if err := db.SaveArchivedBalances(ctx, epoch, balances); err != nil {
t.Fatal(err)
}
newerBalances := make([]uint64, len(balances))
for i := 0; i < len(newerBalances); i++ {
newerBalances[i] = balances[i] * 2
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: params.BeaconConfig().SlotsPerEpoch * 3,
Validators: validators,
Balances: newerBalances,
},
},
}
req := &ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{Epoch: 0},
Indices: []uint64{uint64(1)},
}
res, err := bs.ListValidatorBalances(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// We should expect a response containing the old balance from epoch 0,
// not the new balance from the current state.
want := []*ethpb.ValidatorBalances_Balance{
{
PublicKey: validators[1].PublicKey,
Index: 1,
Balance: balances[1],
},
}
if !reflect.DeepEqual(want, res.Balances) {
t.Errorf("Wanted %v, received %v", want, res.Balances)
}
}
func TestServer_ListValidatorBalances_FromArchive_NewValidatorNotFound(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
epoch := uint64(0)
_, balances := setupValidators(t, db, 100)
if err := db.SaveArchivedBalances(ctx, epoch, balances); err != nil {
t.Fatal(err)
}
newValidators, newBalances := setupValidators(t, db, 200)
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: params.BeaconConfig().SlotsPerEpoch * 3,
Validators: newValidators,
Balances: newBalances,
},
},
}
req := &ethpb.ListValidatorBalancesRequest{
QueryFilter: &ethpb.ListValidatorBalancesRequest_Epoch{Epoch: 0},
Indices: []uint64{1, 150, 161},
}
if _, err := bs.ListValidatorBalances(context.Background(), req); !strings.Contains(err.Error(), "does not exist") {
t.Errorf("Wanted out of range error for including newer validators in the arguments, received %v", err)
}
}
func TestServer_ListValidators_CannotRequestFutureEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := "Cannot retrieve information about an epoch in the future"
if _, err := bs.ListValidators(
ctx,
&ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Epoch{
Epoch: 1,
},
},
); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidators_NoResults(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := &ethpb.Validators{
ValidatorList: make([]*ethpb.Validators_ValidatorContainer, 0),
TotalSize: int32(0),
NextPageToken: strconv.Itoa(0),
}
res, err := bs.ListValidators(
ctx,
&ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Epoch{
Epoch: 0,
},
},
)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
}
func TestServer_ListValidators_OnlyActiveValidators(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
count := 100
balances := make([]uint64, count)
validators := make([]*ethpb.Validator, count)
activeValidators := make([]*ethpb.Validators_ValidatorContainer, 0)
for i := 0; i < count; i++ {
if err := db.SaveValidatorIndex(ctx, [48]byte{byte(i)}, uint64(i)); err != nil {
t.Fatal(err)
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
// We mark even validators as active, and odd validators as inactive.
if i%2 == 0 {
val := &ethpb.Validator{
PublicKey: []byte{byte(i)},
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
validators[i] = val
activeValidators = append(activeValidators, &ethpb.Validators_ValidatorContainer{
Index: uint64(i),
Validator: val,
})
} else {
validators[i] = &ethpb.Validator{
PublicKey: []byte{byte(i)},
ActivationEpoch: 0,
ExitEpoch: 0,
}
}
}
headState := &pbp2p.BeaconState{Validators: validators, Balances: balances}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
received, err := bs.ListValidators(context.Background(), &ethpb.ListValidatorsRequest{
Active: true,
})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(activeValidators, received.ValidatorList) {
t.Errorf("Wanted %v, received %v", activeValidators, received.ValidatorList)
}
}
func TestServer_ListValidators_NoPagination(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
validators, _ := setupValidators(t, db, 100)
want := make([]*ethpb.Validators_ValidatorContainer, len(validators))
for i := 0; i < len(validators); i++ {
want[i] = &ethpb.Validators_ValidatorContainer{
Index: uint64(i),
Validator: validators[i],
}
}
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
}
received, err := bs.ListValidators(context.Background(), &ethpb.ListValidatorsRequest{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(want, received.ValidatorList) {
t.Fatal("Incorrect respond of validators")
}
}
func TestServer_ListValidators_Pagination(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
count := 100
setupValidators(t, db, count)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
}
tests := []struct {
req *ethpb.ListValidatorsRequest
res *ethpb.Validators
}{
{req: &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 3},
res: &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Validator: &ethpb.Validator{
PublicKey: []byte{3},
},
Index: 3,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{4},
},
Index: 4,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{5},
},
Index: 5,
},
},
NextPageToken: strconv.Itoa(2),
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(10), PageSize: 5},
res: &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Validator: &ethpb.Validator{
PublicKey: []byte{50},
},
Index: 50,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{51},
},
Index: 51,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{52},
},
Index: 52,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{53},
},
Index: 53,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{54},
},
Index: 54,
},
},
NextPageToken: strconv.Itoa(11),
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(33), PageSize: 3},
res: &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Validator: &ethpb.Validator{
PublicKey: []byte{99},
},
Index: 99,
},
},
NextPageToken: "",
TotalSize: int32(count)}},
{req: &ethpb.ListValidatorsRequest{PageSize: 2},
res: &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Validator: &ethpb.Validator{
PublicKey: []byte{0},
},
Index: 0,
},
{
Validator: &ethpb.Validator{
PublicKey: []byte{1},
},
Index: 1,
},
},
NextPageToken: strconv.Itoa(1),
TotalSize: int32(count)}},
}
for _, test := range tests {
res, err := bs.ListValidators(context.Background(), test.req)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(res, test.res) {
t.Errorf("Incorrect validator response, wanted %v, received %v", test.res, res)
}
}
}
func TestServer_ListValidators_PaginationOutOfRange(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
count := 1
validators, _ := setupValidators(t, db, count)
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
}
req := &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 100}
wanted := fmt.Sprintf("page start %d >= list %d", req.PageSize, len(validators))
if _, err := bs.ListValidators(context.Background(), req); !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidators_ExceedsMaxPageSize(t *testing.T) {
bs := &Server{}
exceedsMax := int32(params.BeaconConfig().MaxPageSize + 1)
wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, params.BeaconConfig().MaxPageSize)
req := &ethpb.ListValidatorsRequest{PageToken: strconv.Itoa(0), PageSize: exceedsMax}
if _, err := bs.ListValidators(context.Background(), req); !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_ListValidators_DefaultPageSize(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
validators, _ := setupValidators(t, db, 1000)
want := make([]*ethpb.Validators_ValidatorContainer, len(validators))
for i := 0; i < len(validators); i++ {
want[i] = &ethpb.Validators_ValidatorContainer{
Index: uint64(i),
Validator: validators[i],
}
}
headState, err := db.HeadState(context.Background())
if err != nil {
t.Fatal(err)
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
}
req := &ethpb.ListValidatorsRequest{}
res, err := bs.ListValidators(context.Background(), req)
if err != nil {
t.Fatal(err)
}
i := 0
j := params.BeaconConfig().DefaultPageSize
if !reflect.DeepEqual(res.ValidatorList, want[i:j]) {
t.Error("Incorrect respond of validators")
}
}
func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
numEpochs := 30
validators := make([]*ethpb.Validator, numEpochs)
for i := 0; i < numEpochs; i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: uint64(i),
}
}
want := make([]*ethpb.Validators_ValidatorContainer, len(validators))
for i := 0; i < len(validators); i++ {
want[i] = &ethpb.Validators_ValidatorContainer{
Index: uint64(i),
Validator: validators[i],
}
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: helpers.StartSlot(30),
Validators: validators,
},
},
}
req := &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Genesis{
Genesis: true,
},
}
res, err := bs.ListValidators(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if len(res.ValidatorList) != 1 {
t.Errorf("Wanted 1 validator at genesis, received %d", len(res.ValidatorList))
}
req = &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Epoch{
Epoch: 20,
},
}
res, err = bs.ListValidators(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(res.ValidatorList, want[:21]) {
t.Errorf("Incorrect number of validators, wanted %d received %d", len(want[:21]), len(res.ValidatorList))
}
}
func TestServer_GetValidator(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
count := 30
validators := make([]*ethpb.Validator, count)
for i := 0; i < count; i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: uint64(i),
PublicKey: []byte(strconv.Itoa(i)),
}
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Validators: validators,
},
},
}
tests := []struct {
req *ethpb.GetValidatorRequest
res *ethpb.Validator
wantErr bool
err string
}{
{
req: &ethpb.GetValidatorRequest{
QueryFilter: &ethpb.GetValidatorRequest_Index{
Index: 0,
},
},
res: validators[0],
wantErr: false,
},
{
req: &ethpb.GetValidatorRequest{
QueryFilter: &ethpb.GetValidatorRequest_Index{
Index: uint64(count - 1),
},
},
res: validators[count-1],
wantErr: false,
},
{
req: &ethpb.GetValidatorRequest{
QueryFilter: &ethpb.GetValidatorRequest_PublicKey{
PublicKey: []byte(strconv.Itoa(5)),
},
},
res: validators[5],
wantErr: false,
},
{
req: &ethpb.GetValidatorRequest{
QueryFilter: &ethpb.GetValidatorRequest_PublicKey{
PublicKey: []byte("bad-key"),
},
},
res: nil,
wantErr: true,
err: "No validator matched filter criteria",
},
{
req: &ethpb.GetValidatorRequest{
QueryFilter: &ethpb.GetValidatorRequest_Index{
Index: uint64(len(validators)),
},
},
res: nil,
wantErr: true,
err: fmt.Sprintf("there are only %d validators", len(validators)),
},
}
for _, test := range tests {
res, err := bs.GetValidator(context.Background(), test.req)
if test.wantErr && err != nil {
if !strings.Contains(err.Error(), test.err) {
t.Fatalf("Wanted %v, received %v", test.err, err)
}
} else if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(test.res, res) {
t.Errorf("Wanted %v, got %v", test.res, res)
}
}
}
func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := "Cannot retrieve information about an epoch in the future"
if _, err := bs.GetValidatorActiveSetChanges(
ctx,
&ethpb.GetValidatorActiveSetChangesRequest{
QueryFilter: &ethpb.GetValidatorActiveSetChangesRequest_Epoch{
Epoch: 1,
},
},
); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_GetValidatorActiveSetChanges(t *testing.T) {
ctx := context.Background()
validators := make([]*ethpb.Validator, 6)
headState := &pbp2p.BeaconState{
Slot: 0,
Validators: validators,
}
for i := 0; i < len(validators); i++ {
activationEpoch := params.BeaconConfig().FarFutureEpoch
withdrawableEpoch := params.BeaconConfig().FarFutureEpoch
exitEpoch := params.BeaconConfig().FarFutureEpoch
slashed := false
// Mark indices divisible by two as activated.
if i%2 == 0 {
activationEpoch = helpers.DelayedActivationExitEpoch(0)
} else if i%3 == 0 {
// Mark indices divisible by 3 as slashed.
withdrawableEpoch = params.BeaconConfig().EpochsPerSlashingsVector
slashed = true
} else if i%5 == 0 {
// Mark indices divisible by 5 as exited.
exitEpoch = 0
withdrawableEpoch = params.BeaconConfig().MinValidatorWithdrawabilityDelay
}
headState.Validators[i] = &ethpb.Validator{
ActivationEpoch: activationEpoch,
PublicKey: []byte(strconv.Itoa(i)),
WithdrawableEpoch: withdrawableEpoch,
Slashed: slashed,
ExitEpoch: exitEpoch,
}
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0},
},
}
res, err := bs.GetValidatorActiveSetChanges(ctx, &ethpb.GetValidatorActiveSetChangesRequest{})
if err != nil {
t.Fatal(err)
}
wantedActive := [][]byte{
[]byte("0"),
[]byte("2"),
[]byte("4"),
}
wantedSlashed := [][]byte{
[]byte("3"),
}
wantedExited := [][]byte{
[]byte("5"),
}
wanted := &ethpb.ActiveSetChanges{
Epoch: 0,
ActivatedPublicKeys: wantedActive,
ExitedPublicKeys: wantedExited,
SlashedPublicKeys: wantedSlashed,
}
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
}
func TestServer_GetValidatorActiveSetChanges_FromArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
validators := make([]*ethpb.Validator, 6)
headState := &pbp2p.BeaconState{
Slot: helpers.StartSlot(100),
Validators: validators,
}
activatedIndices := make([]uint64, 0)
slashedIndices := make([]uint64, 0)
exitedIndices := make([]uint64, 0)
for i := 0; i < len(validators); i++ {
// Mark indices divisible by two as activated.
if i%2 == 0 {
activatedIndices = append(activatedIndices, uint64(i))
} else if i%3 == 0 {
// Mark indices divisible by 3 as slashed.
slashedIndices = append(slashedIndices, uint64(i))
} else if i%5 == 0 {
// Mark indices divisible by 5 as exited.
exitedIndices = append(exitedIndices, uint64(i))
}
headState.Validators[i] = &ethpb.Validator{
PublicKey: []byte(strconv.Itoa(i)),
}
}
archivedChanges := &pbp2p.ArchivedActiveSetChanges{
Activated: activatedIndices,
Exited: exitedIndices,
Slashed: slashedIndices,
}
// We store the changes during the genesis epoch.
if err := db.SaveArchivedActiveValidatorChanges(ctx, 0, archivedChanges); err != nil {
t.Fatal(err)
}
// We store the same changes during epoch 5 for further testing.
if err := db.SaveArchivedActiveValidatorChanges(ctx, 5, archivedChanges); err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: headState,
},
}
res, err := bs.GetValidatorActiveSetChanges(ctx, &ethpb.GetValidatorActiveSetChangesRequest{
QueryFilter: &ethpb.GetValidatorActiveSetChangesRequest_Genesis{Genesis: true},
})
if err != nil {
t.Fatal(err)
}
wantedActive := [][]byte{
[]byte("0"),
[]byte("2"),
[]byte("4"),
}
wantedSlashed := [][]byte{
[]byte("3"),
}
wantedExited := [][]byte{
[]byte("5"),
}
wanted := &ethpb.ActiveSetChanges{
Epoch: 0,
ActivatedPublicKeys: wantedActive,
ExitedPublicKeys: wantedExited,
SlashedPublicKeys: wantedSlashed,
}
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
res, err = bs.GetValidatorActiveSetChanges(ctx, &ethpb.GetValidatorActiveSetChangesRequest{
QueryFilter: &ethpb.GetValidatorActiveSetChangesRequest_Epoch{Epoch: 5},
})
if err != nil {
t.Fatal(err)
}
wanted.Epoch = 5
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
}
func TestServer_GetValidatorQueue_PendingActivation(t *testing.T) {
headState := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{
{
ActivationEpoch: helpers.DelayedActivationExitEpoch(0),
ActivationEligibilityEpoch: 3,
PublicKey: []byte("3"),
},
{
ActivationEpoch: helpers.DelayedActivationExitEpoch(0),
ActivationEligibilityEpoch: 2,
PublicKey: []byte("2"),
},
{
ActivationEpoch: helpers.DelayedActivationExitEpoch(0),
ActivationEligibilityEpoch: 1,
PublicKey: []byte("1"),
},
},
FinalizedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
},
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
res, err := bs.GetValidatorQueue(context.Background(), &ptypes.Empty{})
if err != nil {
t.Fatal(err)
}
// We verify the keys are properly sorted by the validators' activation eligibility epoch.
wanted := [][]byte{
[]byte("1"),
[]byte("2"),
[]byte("3"),
}
activeValidatorCount, err := helpers.ActiveValidatorCount(headState, helpers.CurrentEpoch(headState))
if err != nil {
t.Fatal(err)
}
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
t.Fatal(err)
}
if res.ChurnLimit != wantChurn {
t.Errorf("Wanted churn %d, received %d", wantChurn, res.ChurnLimit)
}
if !reflect.DeepEqual(res.ActivationPublicKeys, wanted) {
t.Errorf("Wanted %v, received %v", wanted, res.ActivationPublicKeys)
}
}
func TestServer_GetValidatorQueue_PendingExit(t *testing.T) {
headState := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{
{
ActivationEpoch: 0,
ExitEpoch: 4,
WithdrawableEpoch: 3,
PublicKey: []byte("3"),
},
{
ActivationEpoch: 0,
ExitEpoch: 4,
WithdrawableEpoch: 2,
PublicKey: []byte("2"),
},
{
ActivationEpoch: 0,
ExitEpoch: 4,
WithdrawableEpoch: 1,
PublicKey: []byte("1"),
},
},
FinalizedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
},
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
res, err := bs.GetValidatorQueue(context.Background(), &ptypes.Empty{})
if err != nil {
t.Fatal(err)
}
// We verify the keys are properly sorted by the validators' withdrawable epoch.
wanted := [][]byte{
[]byte("1"),
[]byte("2"),
[]byte("3"),
}
activeValidatorCount, err := helpers.ActiveValidatorCount(headState, helpers.CurrentEpoch(headState))
if err != nil {
t.Fatal(err)
}
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
t.Fatal(err)
}
if res.ChurnLimit != wantChurn {
t.Errorf("Wanted churn %d, received %d", wantChurn, res.ChurnLimit)
}
if !reflect.DeepEqual(res.ExitPublicKeys, wanted) {
t.Errorf("Wanted %v, received %v", wanted, res.ExitPublicKeys)
}
}
func TestServer_GetValidatorParticipation_CannotRequestCurrentEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: helpers.StartSlot(2)},
},
}
wanted := "Cannot retrieve information about an epoch currently in progress"
if _, err := bs.GetValidatorParticipation(
ctx,
&ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: 2,
},
},
); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{Slot: 0},
},
}
wanted := "Cannot retrieve information about an epoch in the future"
if _, err := bs.GetValidatorParticipation(
ctx,
&ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: 1,
},
},
); err != nil && !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected error %v, received %v", wanted, err)
}
}
func TestServer_GetValidatorParticipation_FromArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
epoch := uint64(4)
part := &ethpb.ValidatorParticipation{
GlobalParticipationRate: 1.0,
VotedEther: 20,
EligibleEther: 20,
}
if err := db.SaveArchivedValidatorParticipation(ctx, epoch-2, part); err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: &pbp2p.BeaconState{
Slot: helpers.StartSlot(epoch + 1),
FinalizedCheckpoint: &ethpb.Checkpoint{
Epoch: epoch + 1,
},
},
},
}
if _, err := bs.GetValidatorParticipation(ctx, &ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: epoch + 2,
},
}); err == nil {
t.Error("Expected error when requesting future epoch, received nil")
}
// We request data from epoch 0, which we didn't archive, so we should expect an error.
if _, err := bs.GetValidatorParticipation(ctx, &ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Genesis{
Genesis: true,
},
}); err == nil {
t.Error("Expected error when data from archive is not found, received nil")
}
want := &ethpb.ValidatorParticipationResponse{
Epoch: epoch - 2,
Finalized: true,
Participation: part,
}
res, err := bs.GetValidatorParticipation(ctx, &ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: epoch - 2,
},
})
if err != nil {
t.Fatal(err)
}
if !proto.Equal(want, res) {
t.Errorf("Wanted %v, received %v", want, res)
}
}
func TestServer_GetValidatorParticipation_FromArchive_FinalizedEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
part := &ethpb.ValidatorParticipation{
GlobalParticipationRate: 1.0,
VotedEther: 20,
EligibleEther: 20,
}
epoch := uint64(1)
// We archive data for epoch 1.
if err := db.SaveArchivedValidatorParticipation(ctx, epoch, part); err != nil {
t.Fatal(err)
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
// 10 epochs into the future.
State: &pbp2p.BeaconState{
Slot: helpers.StartSlot(epoch + 10),
FinalizedCheckpoint: &ethpb.Checkpoint{
// We say there have been 5 epochs since finality.
Epoch: epoch + 5,
},
},
},
}
want := &ethpb.ValidatorParticipationResponse{
Epoch: epoch,
Finalized: true,
Participation: part,
}
// We request epoch 1.
res, err := bs.GetValidatorParticipation(ctx, &ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: epoch,
},
})
if err != nil {
t.Fatal(err)
}
if !proto.Equal(want, res) {
t.Errorf("Wanted %v, received %v", want, res)
}
}
func TestServer_GetValidatorParticipation_PrevEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
ctx := context.Background()
epoch := uint64(1)
attestedBalance := uint64(1)
validatorCount := uint64(100)
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
atts := []*pbp2p.PendingAttestation{{Data: &ethpb.AttestationData{Target: &ethpb.Checkpoint{}}}}
s := &pbp2p.BeaconState{
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
Validators: validators,
Balances: balances,
BlockRoots: make([][]byte, 128),
Slashings: []uint64{0, 1e9, 1e9},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
CurrentEpochAttestations: atts,
FinalizedCheckpoint: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{},
}
m := &mock.ChainService{
State: s,
Balance: &precompute.Balance{
PrevEpoch: validatorCount * params.BeaconConfig().MaxEffectiveBalance,
PrevEpochTargetAttesters: attestedBalance,
},
}
bs := &Server{
BeaconDB: db,
HeadFetcher: m,
ParticipationFetcher: m,
}
res, err := bs.GetValidatorParticipation(ctx, &ethpb.GetValidatorParticipationRequest{})
if err != nil {
t.Fatal(err)
}
wanted := &ethpb.ValidatorParticipation{
VotedEther: attestedBalance,
EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance,
GlobalParticipationRate: float32(attestedBalance) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance),
}
if !reflect.DeepEqual(res.Participation, wanted) {
t.Error("Incorrect validator participation respond")
}
}
func setupValidators(t *testing.T, db db.Database, count int) ([]*ethpb.Validator, []uint64) {
ctx := context.Background()
balances := make([]uint64, count)
validators := make([]*ethpb.Validator, 0, count)
for i := 0; i < count; i++ {
if err := db.SaveValidatorIndex(ctx, [48]byte{byte(i)}, uint64(i)); err != nil {
t.Fatal(err)
}
balances[i] = uint64(i)
validators = append(validators, &ethpb.Validator{
PublicKey: []byte{byte(i)},
})
}
blk := &ethpb.BeaconBlock{
Slot: 0,
}
blockRoot, err := ssz.HashTreeRoot(blk)
if err != nil {
t.Fatal(err)
}
if err := db.SaveState(
context.Background(),
&pbp2p.BeaconState{Validators: validators, Balances: balances},
blockRoot,
); err != nil {
t.Fatal(err)
}
if err := db.SaveHeadBlockRoot(ctx, blockRoot); err != nil {
t.Fatal(err)
}
return validators, balances
}