Beacon state validator id filter (#1803)

## Issue Addressed

Michael's comment here: https://github.com/sigp/lighthouse/issues/1434#issuecomment-708834079
Resolves #1808

## Proposed Changes

- Add query param `id` and `status` to the `validators` endpoint
- Add string serialization and deserialization for `ValidatorStatus`
- Drop `Epoch` from `ValidatorStatus` variants

## Additional Info

Please provide any additional information. For example, future considerations
or information useful for reviewers.
This commit is contained in:
realbigsean
2020-10-29 05:13:04 +00:00
parent 9f45ac2f5e
commit ae0f025375
4 changed files with 246 additions and 78 deletions

View File

@@ -421,40 +421,69 @@ pub fn serve<T: BeaconChainTypes>(
})
});
// GET beacon/states/{state_id}/validators
// GET beacon/states/{state_id}/validators?id,status
let get_beacon_state_validators = beacon_states_path
.clone()
.and(warp::path("validators"))
.and(warp::query::<api_types::ValidatorsQuery>())
.and(warp::path::end())
.and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| {
blocking_json_task(move || {
state_id
.map_state(&chain, |state| {
let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = chain.spec.far_future_epoch;
.and_then(
|state_id: StateId, chain: Arc<BeaconChain<T>>, query: api_types::ValidatorsQuery| {
blocking_json_task(move || {
state_id
.map_state(&chain, |state| {
let epoch = state.current_epoch();
let finalized_epoch = state.finalized_checkpoint.epoch;
let far_future_epoch = chain.spec.far_future_epoch;
Ok(state
.validators
.iter()
.zip(state.balances.iter())
.enumerate()
.map(|(index, (validator, balance))| api_types::ValidatorData {
index: index as u64,
balance: *balance,
status: api_types::ValidatorStatus::from_validator(
Some(validator),
epoch,
finalized_epoch,
far_future_epoch,
),
validator: validator.clone(),
})
.collect::<Vec<_>>())
})
.map(api_types::GenericResponse::from)
})
});
Ok(state
.validators
.iter()
.zip(state.balances.iter())
.enumerate()
// filter by validator id(s) if provided
.filter(|(index, (validator, _))| {
query.id.as_ref().map_or(true, |ids| {
ids.0.iter().any(|id| match id {
ValidatorId::PublicKey(pubkey) => {
&validator.pubkey == pubkey
}
ValidatorId::Index(param_index) => {
*param_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(
Some(validator),
epoch,
finalized_epoch,
far_future_epoch,
);
if query
.status
.as_ref()
.map_or(true, |statuses| statuses.0.contains(&status))
{
Some(api_types::ValidatorData {
index: index as u64,
balance: *balance,
status,
validator: validator.clone(),
})
} else {
None
}
})
.collect::<Vec<_>>())
})
.map(api_types::GenericResponse::from)
})
},
);
// GET beacon/states/{state_id}/validators/{validator_id}
let get_beacon_state_validators_id = beacon_states_path
@@ -537,8 +566,8 @@ pub fn serve<T: BeaconChainTypes>(
} else {
CommitteeCache::initialized(state, epoch, &chain.spec).map(Cow::Owned)
}
.map_err(BeaconChainError::BeaconStateError)
.map_err(warp_utils::reject::beacon_chain_error)?;
.map_err(BeaconChainError::BeaconStateError)
.map_err(warp_utils::reject::beacon_chain_error)?;
// Use either the supplied slot or all slots in the epoch.
let slots = query.slot.map(|slot| vec![slot]).unwrap_or_else(|| {
@@ -566,11 +595,11 @@ pub fn serve<T: BeaconChainTypes>(
let committee = committee_cache
.get_beacon_committee(slot, index)
.ok_or_else(|| {
warp_utils::reject::custom_bad_request(format!(
"committee index {} does not exist in epoch {}",
index, epoch
))
})?;
warp_utils::reject::custom_bad_request(format!(
"committee index {} does not exist in epoch {}",
index, epoch
))
})?;
response.push(api_types::CommitteeData {
index,
@@ -1605,7 +1634,7 @@ pub fn serve<T: BeaconChainTypes>(
return Err(warp_utils::reject::object_invalid(format!(
"gossip verification failed: {:?}",
e
)))
)));
}
};