Files
lighthouse/beacon_node/http_api/src/validators.rs
chonghe 7e2df6b602 Empty list [] to return all validators balances (#7474)
The endpoint `/eth/v1/beacon/states/head/validator_balances` returns an empty data when the data field is `[]`. According to the beacon API spec, it should return the balances of all validators:

Reference: https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidatorBalances
`If the supplied list is empty (i.e. the body is []) or no body is supplied then balances will be returned for all validators.`


  This PR changes so that: `curl -X 'POST' 'http://localhost:5052/eth/v1/beacon/states/head/validator_balances' -d '[]' | jq` returns balances of all validators.
2025-05-20 07:18:29 +00:00

122 lines
5.2 KiB
Rust

use crate::state_id::StateId;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2::types::{
self as api_types, ExecutionOptimisticFinalizedResponse, ValidatorBalanceData, ValidatorData,
ValidatorId, ValidatorStatus,
};
use std::{collections::HashSet, sync::Arc};
pub fn get_beacon_state_validators<T: BeaconChainTypes>(
state_id: StateId,
chain: Arc<BeaconChain<T>>,
query_ids: &Option<Vec<ValidatorId>>,
query_statuses: &Option<Vec<ValidatorStatus>>,
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<ValidatorData>>, warp::Rejection> {
let (data, execution_optimistic, finalized) = state_id
.map_state_and_execution_optimistic_and_finalized(
&chain,
|state, execution_optimistic, finalized| {
let epoch = state.current_epoch();
let far_future_epoch = chain.spec.far_future_epoch;
let ids_filter_set: Option<HashSet<&ValidatorId>> =
query_ids.as_ref().map(HashSet::from_iter);
Ok((
state
.validators()
.iter()
.zip(state.balances().iter())
.enumerate()
// filter by validator id(s) if provided
.filter(|(index, (validator, _))| {
ids_filter_set.as_ref().is_none_or(|ids_set| {
ids_set.contains(&ValidatorId::PublicKey(validator.pubkey))
|| ids_set.contains(&ValidatorId::Index(*index as u64))
})
})
// filter by status(es) if provided and map the result
.filter_map(|(index, (validator, balance))| {
let status = api_types::ValidatorStatus::from_validator(
validator,
epoch,
far_future_epoch,
);
let status_matches = query_statuses.as_ref().is_none_or(|statuses| {
statuses.contains(&status)
|| statuses.contains(&status.superstatus())
});
if status_matches {
Some(ValidatorData {
index: index as u64,
balance: *balance,
status,
validator: validator.clone(),
})
} else {
None
}
})
.collect::<Vec<_>>(),
execution_optimistic,
finalized,
))
},
)?;
Ok(ExecutionOptimisticFinalizedResponse {
data,
execution_optimistic: Some(execution_optimistic),
finalized: Some(finalized),
})
}
pub fn get_beacon_state_validator_balances<T: BeaconChainTypes>(
state_id: StateId,
chain: Arc<BeaconChain<T>>,
optional_ids: Option<&[ValidatorId]>,
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<ValidatorBalanceData>>, warp::Rejection> {
let (data, execution_optimistic, finalized) = state_id
.map_state_and_execution_optimistic_and_finalized(
&chain,
|state, execution_optimistic, finalized| {
let ids_filter_set: Option<HashSet<&ValidatorId>> = match optional_ids {
// if optional_ids (the request data body) is [], returns a `None`, so that later when calling .is_none_or() will return True
// Hence, all validators will pass through .filter(), and balances of all validators are returned, in accordance to the spec
Some([]) => None,
Some(ids) => Some(HashSet::from_iter(ids.iter())),
None => None,
};
Ok((
state
.validators()
.iter()
.zip(state.balances().iter())
.enumerate()
// filter by validator id(s) if provided
.filter(|(index, (validator, _))| {
ids_filter_set.as_ref().is_none_or(|ids_set| {
ids_set.contains(&ValidatorId::PublicKey(validator.pubkey))
|| ids_set.contains(&ValidatorId::Index(*index as u64))
})
})
.map(|(index, (_, balance))| ValidatorBalanceData {
index: index as u64,
balance: *balance,
})
.collect::<Vec<_>>(),
execution_optimistic,
finalized,
))
},
)?;
Ok(api_types::ExecutionOptimisticFinalizedResponse {
data,
execution_optimistic: Some(execution_optimistic),
finalized: Some(finalized),
})
}