Skip to content

Commit

Permalink
Implement POST validators/validator_balances APIs (sigp#4872)
Browse files Browse the repository at this point in the history
* Add POST for fetching validators from state

* Implement POST for balances

* Tests
  • Loading branch information
michaelsproul authored Dec 8, 2023
1 parent e02adbf commit b882519
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 108 deletions.
160 changes: 57 additions & 103 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod test_utils;
mod ui;
mod validator;
mod validator_inclusion;
mod validators;
mod version;

use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3};
Expand All @@ -41,7 +42,8 @@ use bytes::Bytes;
use directory::DEFAULT_ROOT_DIR;
use eth2::types::{
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode,
PublishBlockRequest, ValidatorId, ValidatorStatus,
PublishBlockRequest, ValidatorBalancesRequestBody, ValidatorId, ValidatorStatus,
ValidatorsRequestBody,
};
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
use lighthouse_version::version_with_platform;
Expand Down Expand Up @@ -663,47 +665,32 @@ pub fn serve<T: BeaconChainTypes>(
query_res: Result<api_types::ValidatorBalancesQuery, warp::Rejection>| {
task_spawner.blocking_json_task(Priority::P1, move || {
let query = query_res?;
let (data, execution_optimistic, finalized) = state_id
.map_state_and_execution_optimistic_and_finalized(
&chain,
|state, execution_optimistic, finalized| {
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.iter().any(|id| match id {
ValidatorId::PublicKey(pubkey) => {
&validator.pubkey == pubkey
}
ValidatorId::Index(param_index) => {
*param_index == *index as u64
}
})
})
})
.map(|(index, (_, balance))| {
Some(api_types::ValidatorBalanceData {
index: index as u64,
balance: *balance,
})
})
.collect::<Vec<_>>(),
execution_optimistic,
finalized,
))
},
)?;
crate::validators::get_beacon_state_validator_balances(
state_id,
chain,
query.id.as_deref(),
)
})
},
);

Ok(api_types::ExecutionOptimisticFinalizedResponse {
data,
execution_optimistic: Some(execution_optimistic),
finalized: Some(finalized),
})
// POST beacon/states/{state_id}/validator_balances
let post_beacon_state_validator_balances = beacon_states_path
.clone()
.and(warp::path("validator_balances"))
.and(warp::path::end())
.and(warp::body::json())
.then(
|state_id: StateId,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
query: ValidatorBalancesRequestBody| {
task_spawner.blocking_json_task(Priority::P1, move || {
crate::validators::get_beacon_state_validator_balances(
state_id,
chain,
Some(&query.ids),
)
})
},
);
Expand All @@ -721,69 +708,34 @@ pub fn serve<T: BeaconChainTypes>(
query_res: Result<api_types::ValidatorsQuery, warp::Rejection>| {
task_spawner.blocking_json_task(Priority::P1, move || {
let query = query_res?;
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;

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.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(
validator,
epoch,
far_future_epoch,
);

let status_matches =
query.status.as_ref().map_or(true, |statuses| {
statuses.contains(&status)
|| statuses.contains(&status.superstatus())
});

if status_matches {
Some(api_types::ValidatorData {
index: index as u64,
balance: *balance,
status,
validator: validator.clone(),
})
} else {
None
}
})
.collect::<Vec<_>>(),
execution_optimistic,
finalized,
))
},
)?;
crate::validators::get_beacon_state_validators(
state_id,
chain,
&query.id,
&query.status,
)
})
},
);

Ok(api_types::ExecutionOptimisticFinalizedResponse {
data,
execution_optimistic: Some(execution_optimistic),
finalized: Some(finalized),
})
// POST beacon/states/{state_id}/validators
let post_beacon_state_validators = beacon_states_path
.clone()
.and(warp::path("validators"))
.and(warp::path::end())
.and(warp::body::json())
.then(
|state_id: StateId,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
query: ValidatorsRequestBody| {
task_spawner.blocking_json_task(Priority::P1, move || {
crate::validators::get_beacon_state_validators(
state_id,
chain,
&query.ids,
&query.statuses,
)
})
},
);
Expand Down Expand Up @@ -4709,6 +4661,8 @@ pub fn serve<T: BeaconChainTypes>(
.uor(post_beacon_pool_voluntary_exits)
.uor(post_beacon_pool_sync_committees)
.uor(post_beacon_pool_bls_to_execution_changes)
.uor(post_beacon_state_validators)
.uor(post_beacon_state_validator_balances)
.uor(post_beacon_rewards_attestations)
.uor(post_beacon_rewards_sync_committee)
.uor(post_validator_duties_attester)
Expand Down
119 changes: 119 additions & 0 deletions beacon_node/http_api/src/validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::state_id::StateId;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2::types::{
self as api_types, ExecutionOptimisticFinalizedResponse, ValidatorBalanceData, ValidatorData,
ValidatorId, ValidatorStatus,
};
use std::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;

Ok((
state
.validators()
.iter()
.zip(state.balances().iter())
.enumerate()
// filter by validator id(s) if provided
.filter(|(index, (validator, _))| {
query_ids.as_ref().map_or(true, |ids| {
ids.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(
validator,
epoch,
far_future_epoch,
);

let status_matches = query_statuses.as_ref().map_or(true, |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| {
Ok((
state
.validators()
.iter()
.zip(state.balances().iter())
.enumerate()
// filter by validator id(s) if provided
.filter(|(index, (validator, _))| {
optional_ids.map_or(true, |ids| {
ids.iter().any(|id| match id {
ValidatorId::PublicKey(pubkey) => &validator.pubkey == pubkey,
ValidatorId::Index(param_index) => {
*param_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),
})
}
29 changes: 28 additions & 1 deletion beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,18 @@ impl ApiTester {
.await
.unwrap()
.map(|res| res.data);
let result_post_index_ids = self
.client
.post_beacon_states_validator_balances(state_id.0, validator_index_ids)
.await
.unwrap()
.map(|res| res.data);
let result_post_pubkey_ids = self
.client
.post_beacon_states_validator_balances(state_id.0, validator_pubkey_ids)
.await
.unwrap()
.map(|res| res.data);

let expected = state_opt.map(|(state, _execution_optimistic, _finalized)| {
let mut validators = Vec::with_capacity(validator_indices.len());
Expand All @@ -868,6 +880,8 @@ impl ApiTester {

assert_eq!(result_index_ids, expected, "{:?}", state_id);
assert_eq!(result_pubkey_ids, expected, "{:?}", state_id);
assert_eq!(result_post_index_ids, expected, "{:?}", state_id);
assert_eq!(result_post_pubkey_ids, expected, "{:?}", state_id);
}
}

Expand Down Expand Up @@ -913,7 +927,6 @@ impl ApiTester {
.await
.unwrap()
.map(|res| res.data);

let result_pubkey_ids = self
.client
.get_beacon_states_validators(
Expand All @@ -924,6 +937,18 @@ impl ApiTester {
.await
.unwrap()
.map(|res| res.data);
let post_result_index_ids = self
.client
.post_beacon_states_validators(state_id.0, Some(validator_index_ids), None)
.await
.unwrap()
.map(|res| res.data);
let post_result_pubkey_ids = self
.client
.post_beacon_states_validators(state_id.0, Some(validator_pubkey_ids), None)
.await
.unwrap()
.map(|res| res.data);

let expected = state_opt.map(|state| {
let epoch = state.current_epoch();
Expand Down Expand Up @@ -959,6 +984,8 @@ impl ApiTester {

assert_eq!(result_index_ids, expected, "{:?}", state_id);
assert_eq!(result_pubkey_ids, expected, "{:?}", state_id);
assert_eq!(post_result_index_ids, expected, "{:?}", state_id);
assert_eq!(post_result_pubkey_ids, expected, "{:?}", state_id);
}
}
}
Expand Down
Loading

0 comments on commit b882519

Please sign in to comment.