Skip to content

Commit

Permalink
Use POST for validator balances.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdee committed Jul 10, 2024
1 parent 704ceba commit fba4f24
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 57 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
0.21.7:
- use POST for specific validator and validator balance information

0.21.6:
- use SSZ on a per-call basis

0.21.5:
- ensure POST bodies are logged as JSON

0.21.4:
- additional nil checks
- allow non-mainnet configurations
Expand Down
3 changes: 3 additions & 0 deletions api/validatorbalancesopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ type ValidatorBalancesOpts struct {
// Indices is a list of validator indices to restrict the returned values.
// If no indices are supplied then no filter will be applied.
Indices []phase0.ValidatorIndex
// PubKeys is a list of validator public keys to restrict the returned values.
// If no public keys are supplied then no filter will be applied.
PubKeys []phase0.BLSPubKey
}
2 changes: 1 addition & 1 deletion http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
)

// defaultUserAgent is sent with requests if no other user agent has been supplied.
const defaultUserAgent = "go-eth2-client/0.21.6"
const defaultUserAgent = "go-eth2-client/0.21.7"

// post sends an HTTP post request and returns the body.
func (s *Service) post(ctx context.Context, endpoint string, body io.Reader) (io.Reader, error) {
Expand Down
64 changes: 14 additions & 50 deletions http/validatorbalances.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ package http
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strings"

client "github.com/attestantio/go-eth2-client"
"github.com/attestantio/go-eth2-client/api"
Expand All @@ -43,21 +43,23 @@ func (s *Service) ValidatorBalances(ctx context.Context,
return nil, errors.Join(errors.New("no state specified"), client.ErrInvalidOptions)
}

if len(opts.Indices) > s.indexChunkSize(ctx) {
return s.chunkedValidatorBalances(ctx, opts)
}

endpoint := fmt.Sprintf("/eth/v1/beacon/states/%s/validator_balances", opts.State)
query := ""
if len(opts.Indices) > 0 {
ids := make([]string, len(opts.Indices))
for i := range opts.Indices {
ids[i] = fmt.Sprintf("%d", opts.Indices[i])
}
query = "id=" + strings.Join(ids, ",")

body := make([]string, 0, len(opts.Indices)+len(opts.PubKeys))
for i := range opts.Indices {
body = append(body, fmt.Sprintf("%d", opts.Indices[i]))
}
for i := range opts.PubKeys {
body = append(body, opts.PubKeys[i].String())
}

httpResponse, err := s.get(ctx, endpoint, query, &opts.Common, false)
data, err := json.Marshal(body)
if err != nil {
return nil, errors.Join(errors.New("failed to marshal request data"), err)
}

httpResponse, err := s.post2(ctx, endpoint, query, &opts.Common, bytes.NewReader(data), ContentTypeJSON, map[string]string{})
if err != nil {
return nil, err
}
Expand All @@ -70,44 +72,6 @@ func (s *Service) ValidatorBalances(ctx context.Context,
}
}

// chunkedValidatorBalances obtains the validator balances a chunk at a time.
func (s *Service) chunkedValidatorBalances(ctx context.Context,
opts *api.ValidatorBalancesOpts,
) (
*api.Response[map[phase0.ValidatorIndex]phase0.Gwei],
error,
) {
data := make(map[phase0.ValidatorIndex]phase0.Gwei)
metadata := make(map[string]any)
chunkSize := s.indexChunkSize(ctx)
for i := 0; i < len(opts.Indices); i += chunkSize {
chunkStart := i
chunkEnd := i + chunkSize
if len(opts.Indices) < chunkEnd {
chunkEnd = len(opts.Indices)
}
chunkOpts := &api.ValidatorBalancesOpts{
State: opts.State,
Indices: opts.Indices[chunkStart:chunkEnd],
}
chunkResponse, err := s.ValidatorBalances(ctx, chunkOpts)
if err != nil {
return nil, errors.Join(errors.New("failed to obtain chunk"), err)
}
for k, v := range chunkResponse.Data {
data[k] = v
}
for k, v := range chunkResponse.Metadata {
metadata[k] = v
}
}

return &api.Response[map[phase0.ValidatorIndex]phase0.Gwei]{
Data: data,
Metadata: metadata,
}, nil
}

func (*Service) validatorBalancesFromJSON(_ context.Context,
httpResponse *httpResponse,
) (
Expand Down
16 changes: 10 additions & 6 deletions http/validatorbalances_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2020 - 2023 Attestant Limited.
// Copyright © 2020 - 2024 Attestant Limited.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down Expand Up @@ -31,11 +31,12 @@ func TestValidatorBalances(t *testing.T) {
defer cancel()

tests := []struct {
name string
opts *api.ValidatorBalancesOpts
err string
errCode int
expected map[phase0.ValidatorIndex]phase0.Gwei
name string
opts *api.ValidatorBalancesOpts
err string
errCode int
expected map[phase0.ValidatorIndex]phase0.Gwei
expectedBalances int
}{
{
name: "NoOpts",
Expand Down Expand Up @@ -103,6 +104,9 @@ func TestValidatorBalances(t *testing.T) {
if test.expected != nil {
require.Equal(t, test.expected, response.Data)
}
if test.expectedBalances > 0 {
require.Len(t, response.Data, test.expectedBalances)
}
}
})
}
Expand Down

0 comments on commit fba4f24

Please sign in to comment.