From 6aa422c69f5f4ac6e5eee33d3c07f2b9b33d9c99 Mon Sep 17 00:00:00 2001 From: Cihat Imamoglu Date: Sat, 30 Sep 2023 15:19:14 +0300 Subject: [PATCH 1/3] Add validatorStates filter to Validators --- http/validators.go | 21 +++++++++++++++++---- http/validators_test.go | 12 +++++++++++- multi/validators.go | 6 +++++- multi/validators_test.go | 3 ++- service.go | 5 ++++- testclients/erroring.go | 7 +++++-- testclients/sleepy.go | 7 +++++-- 7 files changed, 49 insertions(+), 12 deletions(-) diff --git a/http/validators.go b/http/validators.go index 8504c7c4..17a392a4 100644 --- a/http/validators.go +++ b/http/validators.go @@ -20,6 +20,7 @@ import ( "strings" api "github.com/attestantio/go-eth2-client/api/v1" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/pkg/errors" ) @@ -70,7 +71,8 @@ func (s *Service) indexChunkSize(ctx context.Context) int { // Validators provides the validators, with their balance and status, for a given state. // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validators to restrict the returned values. If no validators are supplied no filter will be applied. -func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*api.Validator, error) { +// validatorStates is a list of validator states to restrict the returned values. If no states are supplied no filter will be applied. +func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { if stateID == "" { return nil, errors.New("no state ID specified") } @@ -80,7 +82,7 @@ func (s *Service) Validators(ctx context.Context, stateID string, validatorIndic } if len(validatorIndices) > s.indexChunkSize(ctx) { - return s.chunkedValidators(ctx, stateID, validatorIndices) + return s.chunkedValidators(ctx, stateID, validatorIndices, validatorStates) } url := fmt.Sprintf("/eth/v1/beacon/states/%s/validators", stateID) @@ -91,6 +93,17 @@ func (s *Service) Validators(ctx context.Context, stateID string, validatorIndic } url = fmt.Sprintf("%s?id=%s", url, strings.Join(ids, ",")) } + if len(validatorStates) != 0 { + states := make([]string, len(validatorStates)) + for i := range validatorStates { + states[i] = fmt.Sprintf("%s", validatorStates[i]) + } + if len(validatorIndices) != 0 { + url = fmt.Sprintf("%s&status=%s", url, strings.Join(states, ",")) + } else { + url = fmt.Sprintf("%s?status=%s", url, strings.Join(states, ",")) + } + } respBodyReader, err := s.get(ctx, url) if err != nil { @@ -168,7 +181,7 @@ func (s *Service) validatorsFromState(ctx context.Context, stateID string) (map[ } // chunkedValidators obtains the validators a chunk at a time. -func (s *Service) chunkedValidators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*api.Validator, error) { +func (s *Service) chunkedValidators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { res := make(map[phase0.ValidatorIndex]*api.Validator) indexChunkSize := s.indexChunkSize(ctx) for i := 0; i < len(validatorIndices); i += indexChunkSize { @@ -178,7 +191,7 @@ func (s *Service) chunkedValidators(ctx context.Context, stateID string, validat chunkEnd = len(validatorIndices) } chunk := validatorIndices[chunkStart:chunkEnd] - chunkRes, err := s.Validators(ctx, stateID, chunk) + chunkRes, err := s.Validators(ctx, stateID, chunk, validatorStates) if err != nil { return nil, errors.Wrap(err, "failed to obtain chunk") } diff --git a/http/validators_test.go b/http/validators_test.go index 3a62b319..57041d72 100644 --- a/http/validators_test.go +++ b/http/validators_test.go @@ -20,6 +20,7 @@ import ( "testing" client "github.com/attestantio/go-eth2-client" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/http" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/stretchr/testify/require" @@ -34,6 +35,7 @@ func TestValidators(t *testing.T) { stateID string expectedErrorCode int validatorIndices []phase0.ValidatorIndex + validatorStates []v1.ValidatorState }{ { name: "Invalid", @@ -61,6 +63,14 @@ func TestValidators(t *testing.T) { name: "Justified", stateID: "justified", }, + { + name: "SomeStates", + stateID: "head", + validatorStates: []v1.ValidatorState{ + v1.ValidatorStateActiveOngoing, + v1.ValidatorStateExitedSlashed, + }, + }, { name: "ManyValidators", stateID: "head", @@ -78,7 +88,7 @@ func TestValidators(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - validators, err := service.(client.ValidatorsProvider).Validators(ctx, test.stateID, test.validatorIndices) + validators, err := service.(client.ValidatorsProvider).Validators(ctx, test.stateID, test.validatorIndices, test.validatorStates) if test.expectedErrorCode != 0 { require.Contains(t, err.Error(), fmt.Sprintf("%d", test.expectedErrorCode)) } else { diff --git a/multi/validators.go b/multi/validators.go index 730d435b..91fd550a 100644 --- a/multi/validators.go +++ b/multi/validators.go @@ -18,21 +18,25 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" api "github.com/attestantio/go-eth2-client/api/v1" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" ) // Validators provides the validators, with their balance and status, for a given state. // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validators to restrict the returned values. If no validators are supplied no filter will be applied. +// validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter +// will be applied. func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, + validatorStates []v1.ValidatorState, ) ( map[phase0.ValidatorIndex]*api.Validator, error, ) { res, err := s.doCall(ctx, func(ctx context.Context, client consensusclient.Service) (interface{}, error) { - block, err := client.(consensusclient.ValidatorsProvider).Validators(ctx, stateID, validatorIndices) + block, err := client.(consensusclient.ValidatorsProvider).Validators(ctx, stateID, validatorIndices, validatorStates) if err != nil { return nil, err } diff --git a/multi/validators_test.go b/multi/validators_test.go index 7ce846cc..f528705a 100644 --- a/multi/validators_test.go +++ b/multi/validators_test.go @@ -18,6 +18,7 @@ import ( "testing" consensusclient "github.com/attestantio/go-eth2-client" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/mock" "github.com/attestantio/go-eth2-client/multi" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -51,7 +52,7 @@ func TestValidators(t *testing.T) { require.NoError(t, err) for i := 0; i < 128; i++ { - res, err := multiClient.(consensusclient.ValidatorsProvider).Validators(ctx, "1", []phase0.ValidatorIndex{}) + res, err := multiClient.(consensusclient.ValidatorsProvider).Validators(ctx, "1", []phase0.ValidatorIndex{}, []v1.ValidatorState{}) require.NoError(t, err) require.NotNil(t, res) } diff --git a/service.go b/service.go index c6554dd9..8f069baf 100644 --- a/service.go +++ b/service.go @@ -19,6 +19,7 @@ import ( api "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/capella" @@ -368,7 +369,9 @@ type ValidatorsProvider interface { // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validator indices to restrict the returned values. If no validators IDs are supplied no filter // will be applied. - Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*apiv1.Validator, error) + // validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter + // will be applied. + Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) // ValidatorsByPubKey provides the validators, with their balance and status, for a given state. // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". diff --git a/testclients/erroring.go b/testclients/erroring.go index e3994671..854e219a 100644 --- a/testclients/erroring.go +++ b/testclients/erroring.go @@ -23,6 +23,7 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/deneb" @@ -580,7 +581,9 @@ func (s *Erroring) ValidatorBalances(ctx context.Context, stateID string, valida // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validator indices to restrict the returned values. If no validators IDs are supplied no filter // will be applied. -func (s *Erroring) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { +// validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter +// will be applied. +func (s *Erroring) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { if err := s.maybeError(ctx); err != nil { return nil, err } @@ -588,7 +591,7 @@ func (s *Erroring) Validators(ctx context.Context, stateID string, validatorIndi if !isNext { return nil, fmt.Errorf("%s@%s does not support this call", s.next.Name(), s.next.Address()) } - return next.Validators(ctx, stateID, validatorIndices) + return next.Validators(ctx, stateID, validatorIndices, validatorStates) } // ValidatorsByPubKey provides the validators, with their balance and status, for a given state. diff --git a/testclients/sleepy.go b/testclients/sleepy.go index 20a323be..fd1ac4f0 100644 --- a/testclients/sleepy.go +++ b/testclients/sleepy.go @@ -23,6 +23,7 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" + v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -395,13 +396,15 @@ func (s *Sleepy) ValidatorBalances(ctx context.Context, stateID string, validato // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validator indices to restrict the returned values. If no validators IDs are supplied no filter // will be applied. -func (s *Sleepy) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { +// validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter +// will be applied. +func (s *Sleepy) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { s.sleep(ctx) next, isNext := s.next.(consensusclient.ValidatorsProvider) if !isNext { return nil, errors.New("next does not support this call") } - return next.Validators(ctx, stateID, validatorIndices) + return next.Validators(ctx, stateID, validatorIndices, validatorStates) } // ValidatorsByPubKey provides the validators, with their balance and status, for a given state. From 7f6575eedfa3e050acc861ef09dedf1d447960e3 Mon Sep 17 00:00:00 2001 From: Cihat Imamoglu Date: Mon, 2 Oct 2023 10:04:10 +0300 Subject: [PATCH 2/3] Fix imports --- http/validators.go | 5 ++--- mock/validators.go | 2 +- multi/validators.go | 3 +-- service.go | 3 +-- testclients/erroring.go | 3 +-- testclients/sleepy.go | 3 +-- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/http/validators.go b/http/validators.go index 17a392a4..9a974b82 100644 --- a/http/validators.go +++ b/http/validators.go @@ -20,7 +20,6 @@ import ( "strings" api "github.com/attestantio/go-eth2-client/api/v1" - v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/pkg/errors" ) @@ -72,7 +71,7 @@ func (s *Service) indexChunkSize(ctx context.Context) int { // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validators to restrict the returned values. If no validators are supplied no filter will be applied. // validatorStates is a list of validator states to restrict the returned values. If no states are supplied no filter will be applied. -func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { +func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []api.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { if stateID == "" { return nil, errors.New("no state ID specified") } @@ -181,7 +180,7 @@ func (s *Service) validatorsFromState(ctx context.Context, stateID string) (map[ } // chunkedValidators obtains the validators a chunk at a time. -func (s *Service) chunkedValidators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { +func (s *Service) chunkedValidators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []api.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { res := make(map[phase0.ValidatorIndex]*api.Validator) indexChunkSize := s.indexChunkSize(ctx) for i := 0; i < len(validatorIndices); i += indexChunkSize { diff --git a/mock/validators.go b/mock/validators.go index 4c9ac1d5..d9da02f3 100644 --- a/mock/validators.go +++ b/mock/validators.go @@ -24,6 +24,6 @@ import ( // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". // validatorIndices is a list of validator indices to restrict the returned values. If no validators IDs are supplied no filter // will be applied. -func (s *Service) Validators(_ context.Context, _ string, _ []phase0.ValidatorIndex) (map[phase0.ValidatorIndex]*api.Validator, error) { +func (s *Service) Validators(_ context.Context, _ string, _ []phase0.ValidatorIndex, _ []api.ValidatorState) (map[phase0.ValidatorIndex]*api.Validator, error) { return map[phase0.ValidatorIndex]*api.Validator{}, nil } diff --git a/multi/validators.go b/multi/validators.go index 91fd550a..b4c5dc58 100644 --- a/multi/validators.go +++ b/multi/validators.go @@ -18,7 +18,6 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" api "github.com/attestantio/go-eth2-client/api/v1" - v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" ) @@ -30,7 +29,7 @@ import ( func (s *Service) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, - validatorStates []v1.ValidatorState, + validatorStates []api.ValidatorState, ) ( map[phase0.ValidatorIndex]*api.Validator, error, diff --git a/service.go b/service.go index 8f069baf..ccd6230b 100644 --- a/service.go +++ b/service.go @@ -19,7 +19,6 @@ import ( api "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" - v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/capella" @@ -371,7 +370,7 @@ type ValidatorsProvider interface { // will be applied. // validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter // will be applied. - Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) + Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []apiv1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) // ValidatorsByPubKey provides the validators, with their balance and status, for a given state. // stateID can be a slot number or state root, or one of the special values "genesis", "head", "justified" or "finalized". diff --git a/testclients/erroring.go b/testclients/erroring.go index 854e219a..9d58007f 100644 --- a/testclients/erroring.go +++ b/testclients/erroring.go @@ -23,7 +23,6 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" - v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/deneb" @@ -583,7 +582,7 @@ func (s *Erroring) ValidatorBalances(ctx context.Context, stateID string, valida // will be applied. // validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter // will be applied. -func (s *Erroring) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { +func (s *Erroring) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []apiv1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { if err := s.maybeError(ctx); err != nil { return nil, err } diff --git a/testclients/sleepy.go b/testclients/sleepy.go index fd1ac4f0..77ba87cb 100644 --- a/testclients/sleepy.go +++ b/testclients/sleepy.go @@ -23,7 +23,6 @@ import ( consensusclient "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" - v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -398,7 +397,7 @@ func (s *Sleepy) ValidatorBalances(ctx context.Context, stateID string, validato // will be applied. // validatorStates is a list of validator states to restrict the returned values. If no validators states are supplied no filter // will be applied. -func (s *Sleepy) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []v1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { +func (s *Sleepy) Validators(ctx context.Context, stateID string, validatorIndices []phase0.ValidatorIndex, validatorStates []apiv1.ValidatorState) (map[phase0.ValidatorIndex]*apiv1.Validator, error) { s.sleep(ctx) next, isNext := s.next.(consensusclient.ValidatorsProvider) if !isNext { From 0231f944eeec8a7007244d039578a257dc4063e1 Mon Sep 17 00:00:00 2001 From: Cihat Imamoglu Date: Mon, 2 Oct 2023 10:07:59 +0300 Subject: [PATCH 3/3] gosimple fix --- http/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/validators.go b/http/validators.go index 9a974b82..ee80c5ce 100644 --- a/http/validators.go +++ b/http/validators.go @@ -95,7 +95,7 @@ func (s *Service) Validators(ctx context.Context, stateID string, validatorIndic if len(validatorStates) != 0 { states := make([]string, len(validatorStates)) for i := range validatorStates { - states[i] = fmt.Sprintf("%s", validatorStates[i]) + states[i] = validatorStates[i].String() } if len(validatorIndices) != 0 { url = fmt.Sprintf("%s&status=%s", url, strings.Join(states, ","))