Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(node-api): Validator balance endpoints - WIP #2483

Draft
wants to merge 91 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
1ac13b4
Implement validators endpoint filtering properly
shotes Feb 3, 2025
58bdc83
Reorganize and add length check
shotes Feb 3, 2025
642bf9a
start adding status type
shotes Feb 3, 2025
900f949
Add validator statuses
shotes Feb 3, 2025
f34d3c2
error on bad status
shotes Feb 3, 2025
8546aee
Fix comments
shotes Feb 3, 2025
93efc2a
add methods to consensus clients and test added
nidhi-singh02 Feb 4, 2025
8f31ecb
go-eth2-client latest version
nidhi-singh02 Feb 4, 2025
a357bc6
add test for empty status - need to fix
nidhi-singh02 Feb 4, 2025
08064f7
nilaway fix
nidhi-singh02 Feb 4, 2025
fcb400f
Merge branch 'main' into node-api-validators
calbera Feb 4, 2025
b631d7d
Merge branch 'main' into node-api-validators
calbera Feb 4, 2025
b8ed978
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 5, 2025
2808857
validators func overriden - not used
nidhi-singh02 Feb 5, 2025
4075c81
cleanup
nidhi-singh02 Feb 5, 2025
cd5ff6b
nilaway go away
nidhi-singh02 Feb 5, 2025
12c2ad2
linter
nidhi-singh02 Feb 5, 2025
3d8ba19
response via func call - with correct values
nidhi-singh02 Feb 5, 2025
73e1d39
more tests for validators
nidhi-singh02 Feb 5, 2025
6cc0d18
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 6, 2025
029b554
status func added to validator type + UTs
nidhi-singh02 Feb 6, 2025
2ec847b
on no match donot error
nidhi-singh02 Feb 6, 2025
4ed7c83
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 6, 2025
e8af248
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 7, 2025
55030da
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 8, 2025
c1b0563
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 11, 2025
c6e2408
more verbose error
nidhi-singh02 Feb 11, 2025
de5a3ce
constants duplicate remove
nidhi-singh02 Feb 11, 2025
1c778e0
e2e cleanup
nidhi-singh02 Feb 11, 2025
17a4ee0
e2e updated
nidhi-singh02 Feb 11, 2025
b5ab7e9
Merge branch 'main' into node-api-validators
calbera Feb 12, 2025
dbf1da5
typo and rename var
nidhi-singh02 Feb 12, 2025
6cf51bc
e2e test clean
nidhi-singh02 Feb 12, 2025
fc34ae9
filteredvalidators updated
nidhi-singh02 Feb 12, 2025
c42abb7
func reference added for status
nidhi-singh02 Feb 12, 2025
35c98be
lint issue fix
nidhi-singh02 Feb 12, 2025
2bbebed
cog complexity reduced
nidhi-singh02 Feb 12, 2025
4822ea9
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 13, 2025
07cc2eb
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 14, 2025
2207ca1
Merge branch 'main' into node-api-validators
nidhi-singh02 Feb 17, 2025
9177278
refactor(cli): Refactor CollectGenesisDeposits to be callable from ru…
rezbera Feb 5, 2025
1b98570
status func added to validator type + UTs
nidhi-singh02 Feb 6, 2025
705df2d
on no match donot error
nidhi-singh02 Feb 6, 2025
2c226ce
validator get balances just by state_id
nidhi-singh02 Feb 6, 2025
78f539c
linting fix
nidhi-singh02 Feb 6, 2025
9030c28
chore(networks): 80094 network configs (#2484)
calbera Feb 6, 2025
f4f7d6b
gosec
nidhi-singh02 Feb 6, 2025
2dd0c9f
fix(ci): Update Test Configs to work with new Geth version (#2489)
rezbera Feb 7, 2025
6014859
fix for post balance and e2e
nidhi-singh02 Feb 7, 2025
88cf482
refactor(depinject): Replace Concrete CometBFT implementation with In…
rezbera Feb 7, 2025
c07010a
chore(e2e): Initialize `finalBlockNum` before wait loop in `WaitForFi…
shotes Feb 7, 2025
c78fac6
fix(cli): unlocking rollback (#2478)
abi87 Feb 8, 2025
136f981
correct response when no record, api fix
nidhi-singh02 Feb 10, 2025
423fd5e
cleanup
nidhi-singh02 Feb 10, 2025
cdfaf85
lint fix
nidhi-singh02 Feb 10, 2025
7f25981
chore(generics): Remove Generics from Node Builder (#2500)
rezbera Feb 10, 2025
247ae34
fix(engine api): [1/3] error on engine API errors (#2485)
shotes Feb 10, 2025
823680e
more verbose error
nidhi-singh02 Feb 11, 2025
640fefb
constants duplicate remove
nidhi-singh02 Feb 11, 2025
b0f7f33
e2e cleanup
nidhi-singh02 Feb 11, 2025
d6ca21f
e2e updated
nidhi-singh02 Feb 11, 2025
734d6b1
Explicitly set bash shell in Kurtosis Makefile
fridrik01 Feb 4, 2025
da9a476
typo and rename var
nidhi-singh02 Feb 12, 2025
fc266c8
filteredvalidators updated
nidhi-singh02 Feb 12, 2025
7b1bfc1
func reference added for status
nidhi-singh02 Feb 12, 2025
e5037b9
lint issue fix
nidhi-singh02 Feb 12, 2025
1b523c9
cog complexity reduced
nidhi-singh02 Feb 12, 2025
0d4bad2
Fix broken CI due to using newest go version which is not supported b…
fridrik01 Feb 12, 2025
6438669
chore: Cleanup devtools.mk make targets (#2421)
fridrik01 Feb 12, 2025
38e4fdf
do not reject PRs based on missing period in a comment (#2504)
fridrik01 Feb 12, 2025
dbe5cf1
Revert "fix(engine api): [1/3] error on engine API errors (#2485)" (#…
rezbera Feb 12, 2025
735fe5d
fix(types): avoid panic on unmarshalling of empty inputs (#2511)
abi87 Feb 14, 2025
f604b4f
fix(state-processor): stricter timestamp enforcement on execution-pay…
abi87 Feb 14, 2025
7d992b9
chore(types): Fix error type and clean up interface definition (#2515)
rezbera Feb 15, 2025
e57bb36
chore(engine): splitted up NotifyAndVerifyNewPayload (#2495)
abi87 Feb 15, 2025
1c3ef84
lint
nidhi-singh02 Feb 17, 2025
77c6b39
Merge branch 'node-api-validators' into validator-endpoints
nidhi-singh02 Feb 17, 2025
56f2814
import
nidhi-singh02 Feb 17, 2025
64d4375
Merge branch 'main' into validator-endpoints
nidhi-singh02 Feb 19, 2025
386aebd
build fix
nidhi-singh02 Feb 19, 2025
4a63d72
Merge branch 'main' into validator-endpoints
nidhi-singh02 Feb 19, 2025
14c7bef
lint
nidhi-singh02 Feb 19, 2025
13e584c
handle no data found
nidhi-singh02 Feb 20, 2025
e944e8f
404 scenario
nidhi-singh02 Feb 20, 2025
aa62ef1
Merge branch 'main' into validator-endpoints
nidhi-singh02 Feb 20, 2025
b0b15c5
using switch
nidhi-singh02 Feb 20, 2025
9e88e56
Merge branch 'main' into validator-endpoints
nidhi-singh02 Feb 20, 2025
b78d051
Merge branch 'main' into validator-endpoints
nidhi-singh02 Feb 21, 2025
973a645
e2e tests
nidhi-singh02 Feb 21, 2025
23bed52
linter
nidhi-singh02 Feb 21, 2025
d1094e0
linter and nilaway
nidhi-singh02 Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion consensus-types/types/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,5 @@ func (v *Validator) Status(currentEpoch math.Epoch) (string, error) {
}
return constants.ValidatorStatusWithdrawalDone, nil
}

return "", ErrInvalidValidatorStatus
}
50 changes: 46 additions & 4 deletions node-api/backend/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"slices"
"strconv"

"cosmossdk.io/collections"
"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
"github.com/berachain/beacon-kit/node-api/backend/utils"
Expand All @@ -33,6 +34,9 @@ import (
statedb "github.com/berachain/beacon-kit/state-transition/core/state"
)

// ErrValidatorNotFound is an error for when a validator is not found.
var ErrValidatorNotFound = errors.New("validator not found")

// ErrStatusFilterMismatch is an error for when a validator status does not
// match the status filter.
var ErrStatusFilterMismatch = errors.New("validator status does not match status filter")
Expand Down Expand Up @@ -200,11 +204,21 @@ func (b Backend) ValidatorByID(
return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
}
index, err := utils.ValidatorIndexByID(st, id)
if err != nil {
switch {
case err == nil:
// continue processing
case errors.Is(err, collections.ErrNotFound):
return nil, ErrValidatorNotFound
default:
return nil, errors.Wrapf(err, "failed to get validator index by id %s", id)
}
validator, err := st.ValidatorByIndex(index)
if err != nil {
switch {
case err == nil:
// continue processing
case errors.Is(err, collections.ErrNotFound):
return nil, ErrValidatorNotFound
Comment on lines +219 to +220
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you don't need to special case here?
Index may not exist but if it exists the validator must exist.
So here you can just

if err != nil {
return err
}

default:
return nil, errors.Wrapf(err, "failed to get validator by index %d", index)
}
balance, err := st.GetBalance(index)
Expand All @@ -228,21 +242,49 @@ func (b Backend) ValidatorByID(
func (b Backend) ValidatorBalancesByIDs(
slot math.Slot, ids []string,
) ([]*beacontypes.ValidatorBalanceData, error) {
var index math.U64
st, _, err := b.stateFromSlot(slot)
if err != nil {
return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
}

// If no IDs provided, return all validator balances
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the specs here?
We return every validator balance or only active ones?
Note that a validator entry in create in beaconState the moment a deposit is successfully processed, even if it has 10K and it cannot be activated yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Balances will be returned for all indices or public key that match known validators.If an index or public key does not match any known validator, no balance will be returned but this will not cause an error. from reference.
Here status is not a filter, so all validator balance matching the filter.

if len(ids) == 0 {
rawBalances, errInBalances := st.GetBalances()
if errInBalances != nil {
return nil, errInBalances
}
// Convert []uint64 to []*ValidatorBalanceData as per the API spec
balances := make([]*beacontypes.ValidatorBalanceData, len(rawBalances))
for i, balance := range rawBalances {
balances[i] = &beacontypes.ValidatorBalanceData{
Index: uint64(i), // #nosec:G115 // Safe as i comes from range loop
Balance: balance,
}
}
return balances, nil
}

balances := make([]*beacontypes.ValidatorBalanceData, 0)
var index math.U64

for _, id := range ids {
index, err = utils.ValidatorIndexByID(st, id)
if err != nil {
// If public key as id is not found in the state, do not return an error.
if errors.Is(err, collections.ErrNotFound) {
continue
}
return nil, errors.Wrapf(err, "failed to get validator index by id %s", id)
}
var balance math.U64
// TODO: same issue as above, shouldn't error on not found.
balance, err = st.GetBalance(index)

if err != nil {
// if index does not exist and GetBalance returns an error containing "collections: not found"
// do not return an error.
if errors.Is(err, collections.ErrNotFound) {
continue
}
return nil, errors.Wrapf(err, "failed to get validator balance for validator index %d", index)
}
balances = append(balances, &beacontypes.ValidatorBalanceData{
Expand Down
4 changes: 2 additions & 2 deletions node-api/handlers/beacon/types/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type PostStateValidatorsRequest struct {

type GetStateValidatorRequest struct {
types.StateIDRequest
ValidatorID string `query:"validator_id" validate:"required,validator_id"`
ValidatorID string `param:"validator_id" validate:"required,validator_id"`
}

type GetValidatorBalancesRequest struct {
Expand All @@ -67,7 +67,7 @@ type GetValidatorBalancesRequest struct {

type PostValidatorBalancesRequest struct {
types.StateIDRequest
IDs []string `validate:"dive,validator_id"`
IDs []string `json:"-" validate:"dive,validator_id"`
}

type GetStateCommitteesRequest struct {
Expand Down
43 changes: 36 additions & 7 deletions node-api/handlers/beacon/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@
package beacon

import (
"errors"
"net/http"
"strings"

"github.com/berachain/beacon-kit/node-api/backend"
"github.com/berachain/beacon-kit/node-api/handlers"
beacontypes "github.com/berachain/beacon-kit/node-api/handlers/beacon/types"
types "github.com/berachain/beacon-kit/node-api/handlers/types"
"github.com/berachain/beacon-kit/node-api/handlers/utils"
)

Expand Down Expand Up @@ -81,10 +87,17 @@ func (h *Handler) GetStateValidator(c handlers.Context) (any, error) {
slot,
req.ValidatorID,
)
if err != nil {
switch {
case errors.Is(err, backend.ErrValidatorNotFound):
return &handlers.HTTPError{
Code: http.StatusNotFound,
Message: "Validator not found",
}, nil
case err != nil:
return nil, err
default:
return beacontypes.NewResponse(validator), nil
}
return validator, nil
}

func (h *Handler) GetStateValidatorBalances(c handlers.Context) (any, error) {
Expand All @@ -95,7 +108,15 @@ func (h *Handler) GetStateValidatorBalances(c handlers.Context) (any, error) {
return nil, err
}
slot, err := utils.SlotFromStateID(req.StateID, h.backend)

// Check for error message "slot not found at state root"
if err != nil {
if strings.Contains(err.Error(), "slot not found at state root") {
return nil, &handlers.HTTPError{
Code: http.StatusNotFound,
Message: "State not found",
}
}
return nil, err
}
balances, err := h.backend.ValidatorBalancesByIDs(
Expand All @@ -109,12 +130,20 @@ func (h *Handler) GetStateValidatorBalances(c handlers.Context) (any, error) {
}

func (h *Handler) PostStateValidatorBalances(c handlers.Context) (any, error) {
req, err := utils.BindAndValidate[beacontypes.PostValidatorBalancesRequest](
c, h.Logger(),
)
if err != nil {
return nil, err
var ids []string
if err := c.Bind(&ids); err != nil {
return nil, types.ErrInvalidRequest
}
// Get state_id from URL path parameter
req := beacontypes.PostValidatorBalancesRequest{
StateIDRequest: types.StateIDRequest{StateID: c.Param("state_id")},
IDs: ids,
}

if err := c.Validate(&req); err != nil {
return nil, types.ErrInvalidRequest
}

slot, err := utils.SlotFromStateID(req.StateID, h.backend)
if err != nil {
return nil, err
Expand Down
45 changes: 45 additions & 0 deletions node-api/handlers/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2025, Berachain Foundation. All rights reserved.
// Use of this software is governed by the Business Source License included
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
//
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
// VERSIONS OF THE LICENSED WORK.
//
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
//
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.

package handlers

// HTTPError represents an HTTP error response.
type HTTPError struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may have this already in our codebase? cc @rezbera

Code int `json:"code"`
Message string `json:"message"`
}

// NewHTTPError creates a new HTTPError.
func NewHTTPError(code int, message string) *HTTPError {
return &HTTPError{
Code: code,
Message: message,
}
}

// Error implements the error interface.
func (e *HTTPError) Error() string {
return e.Message
}

// StatusCode returns the HTTP status code.
func (e *HTTPError) StatusCode() int {
return e.Code
}
Loading
Loading