Skip to content

Commit b882519

Browse files
Implement POST validators/validator_balances APIs (#4872)
* Add POST for fetching validators from state * Implement POST for balances * Tests
1 parent e02adbf commit b882519

File tree

5 files changed

+305
-108
lines changed

5 files changed

+305
-108
lines changed

beacon_node/http_api/src/lib.rs

Lines changed: 57 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod test_utils;
2626
mod ui;
2727
mod validator;
2828
mod validator_inclusion;
29+
mod validators;
2930
mod version;
3031

3132
use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3};
@@ -41,7 +42,8 @@ use bytes::Bytes;
4142
use directory::DEFAULT_ROOT_DIR;
4243
use eth2::types::{
4344
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode,
44-
PublishBlockRequest, ValidatorId, ValidatorStatus,
45+
PublishBlockRequest, ValidatorBalancesRequestBody, ValidatorId, ValidatorStatus,
46+
ValidatorsRequestBody,
4547
};
4648
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
4749
use lighthouse_version::version_with_platform;
@@ -663,47 +665,32 @@ pub fn serve<T: BeaconChainTypes>(
663665
query_res: Result<api_types::ValidatorBalancesQuery, warp::Rejection>| {
664666
task_spawner.blocking_json_task(Priority::P1, move || {
665667
let query = query_res?;
666-
let (data, execution_optimistic, finalized) = state_id
667-
.map_state_and_execution_optimistic_and_finalized(
668-
&chain,
669-
|state, execution_optimistic, finalized| {
670-
Ok((
671-
state
672-
.validators()
673-
.iter()
674-
.zip(state.balances().iter())
675-
.enumerate()
676-
// filter by validator id(s) if provided
677-
.filter(|(index, (validator, _))| {
678-
query.id.as_ref().map_or(true, |ids| {
679-
ids.iter().any(|id| match id {
680-
ValidatorId::PublicKey(pubkey) => {
681-
&validator.pubkey == pubkey
682-
}
683-
ValidatorId::Index(param_index) => {
684-
*param_index == *index as u64
685-
}
686-
})
687-
})
688-
})
689-
.map(|(index, (_, balance))| {
690-
Some(api_types::ValidatorBalanceData {
691-
index: index as u64,
692-
balance: *balance,
693-
})
694-
})
695-
.collect::<Vec<_>>(),
696-
execution_optimistic,
697-
finalized,
698-
))
699-
},
700-
)?;
668+
crate::validators::get_beacon_state_validator_balances(
669+
state_id,
670+
chain,
671+
query.id.as_deref(),
672+
)
673+
})
674+
},
675+
);
701676

702-
Ok(api_types::ExecutionOptimisticFinalizedResponse {
703-
data,
704-
execution_optimistic: Some(execution_optimistic),
705-
finalized: Some(finalized),
706-
})
677+
// POST beacon/states/{state_id}/validator_balances
678+
let post_beacon_state_validator_balances = beacon_states_path
679+
.clone()
680+
.and(warp::path("validator_balances"))
681+
.and(warp::path::end())
682+
.and(warp::body::json())
683+
.then(
684+
|state_id: StateId,
685+
task_spawner: TaskSpawner<T::EthSpec>,
686+
chain: Arc<BeaconChain<T>>,
687+
query: ValidatorBalancesRequestBody| {
688+
task_spawner.blocking_json_task(Priority::P1, move || {
689+
crate::validators::get_beacon_state_validator_balances(
690+
state_id,
691+
chain,
692+
Some(&query.ids),
693+
)
707694
})
708695
},
709696
);
@@ -721,69 +708,34 @@ pub fn serve<T: BeaconChainTypes>(
721708
query_res: Result<api_types::ValidatorsQuery, warp::Rejection>| {
722709
task_spawner.blocking_json_task(Priority::P1, move || {
723710
let query = query_res?;
724-
let (data, execution_optimistic, finalized) = state_id
725-
.map_state_and_execution_optimistic_and_finalized(
726-
&chain,
727-
|state, execution_optimistic, finalized| {
728-
let epoch = state.current_epoch();
729-
let far_future_epoch = chain.spec.far_future_epoch;
730-
731-
Ok((
732-
state
733-
.validators()
734-
.iter()
735-
.zip(state.balances().iter())
736-
.enumerate()
737-
// filter by validator id(s) if provided
738-
.filter(|(index, (validator, _))| {
739-
query.id.as_ref().map_or(true, |ids| {
740-
ids.iter().any(|id| match id {
741-
ValidatorId::PublicKey(pubkey) => {
742-
&validator.pubkey == pubkey
743-
}
744-
ValidatorId::Index(param_index) => {
745-
*param_index == *index as u64
746-
}
747-
})
748-
})
749-
})
750-
// filter by status(es) if provided and map the result
751-
.filter_map(|(index, (validator, balance))| {
752-
let status = api_types::ValidatorStatus::from_validator(
753-
validator,
754-
epoch,
755-
far_future_epoch,
756-
);
757-
758-
let status_matches =
759-
query.status.as_ref().map_or(true, |statuses| {
760-
statuses.contains(&status)
761-
|| statuses.contains(&status.superstatus())
762-
});
763-
764-
if status_matches {
765-
Some(api_types::ValidatorData {
766-
index: index as u64,
767-
balance: *balance,
768-
status,
769-
validator: validator.clone(),
770-
})
771-
} else {
772-
None
773-
}
774-
})
775-
.collect::<Vec<_>>(),
776-
execution_optimistic,
777-
finalized,
778-
))
779-
},
780-
)?;
711+
crate::validators::get_beacon_state_validators(
712+
state_id,
713+
chain,
714+
&query.id,
715+
&query.status,
716+
)
717+
})
718+
},
719+
);
781720

782-
Ok(api_types::ExecutionOptimisticFinalizedResponse {
783-
data,
784-
execution_optimistic: Some(execution_optimistic),
785-
finalized: Some(finalized),
786-
})
721+
// POST beacon/states/{state_id}/validators
722+
let post_beacon_state_validators = beacon_states_path
723+
.clone()
724+
.and(warp::path("validators"))
725+
.and(warp::path::end())
726+
.and(warp::body::json())
727+
.then(
728+
|state_id: StateId,
729+
task_spawner: TaskSpawner<T::EthSpec>,
730+
chain: Arc<BeaconChain<T>>,
731+
query: ValidatorsRequestBody| {
732+
task_spawner.blocking_json_task(Priority::P1, move || {
733+
crate::validators::get_beacon_state_validators(
734+
state_id,
735+
chain,
736+
&query.ids,
737+
&query.statuses,
738+
)
787739
})
788740
},
789741
);
@@ -4709,6 +4661,8 @@ pub fn serve<T: BeaconChainTypes>(
47094661
.uor(post_beacon_pool_voluntary_exits)
47104662
.uor(post_beacon_pool_sync_committees)
47114663
.uor(post_beacon_pool_bls_to_execution_changes)
4664+
.uor(post_beacon_state_validators)
4665+
.uor(post_beacon_state_validator_balances)
47124666
.uor(post_beacon_rewards_attestations)
47134667
.uor(post_beacon_rewards_sync_committee)
47144668
.uor(post_validator_duties_attester)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use crate::state_id::StateId;
2+
use beacon_chain::{BeaconChain, BeaconChainTypes};
3+
use eth2::types::{
4+
self as api_types, ExecutionOptimisticFinalizedResponse, ValidatorBalanceData, ValidatorData,
5+
ValidatorId, ValidatorStatus,
6+
};
7+
use std::sync::Arc;
8+
9+
pub fn get_beacon_state_validators<T: BeaconChainTypes>(
10+
state_id: StateId,
11+
chain: Arc<BeaconChain<T>>,
12+
query_ids: &Option<Vec<ValidatorId>>,
13+
query_statuses: &Option<Vec<ValidatorStatus>>,
14+
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<ValidatorData>>, warp::Rejection> {
15+
let (data, execution_optimistic, finalized) = state_id
16+
.map_state_and_execution_optimistic_and_finalized(
17+
&chain,
18+
|state, execution_optimistic, finalized| {
19+
let epoch = state.current_epoch();
20+
let far_future_epoch = chain.spec.far_future_epoch;
21+
22+
Ok((
23+
state
24+
.validators()
25+
.iter()
26+
.zip(state.balances().iter())
27+
.enumerate()
28+
// filter by validator id(s) if provided
29+
.filter(|(index, (validator, _))| {
30+
query_ids.as_ref().map_or(true, |ids| {
31+
ids.iter().any(|id| match id {
32+
ValidatorId::PublicKey(pubkey) => &validator.pubkey == pubkey,
33+
ValidatorId::Index(param_index) => {
34+
*param_index == *index as u64
35+
}
36+
})
37+
})
38+
})
39+
// filter by status(es) if provided and map the result
40+
.filter_map(|(index, (validator, balance))| {
41+
let status = api_types::ValidatorStatus::from_validator(
42+
validator,
43+
epoch,
44+
far_future_epoch,
45+
);
46+
47+
let status_matches = query_statuses.as_ref().map_or(true, |statuses| {
48+
statuses.contains(&status)
49+
|| statuses.contains(&status.superstatus())
50+
});
51+
52+
if status_matches {
53+
Some(ValidatorData {
54+
index: index as u64,
55+
balance: *balance,
56+
status,
57+
validator: validator.clone(),
58+
})
59+
} else {
60+
None
61+
}
62+
})
63+
.collect::<Vec<_>>(),
64+
execution_optimistic,
65+
finalized,
66+
))
67+
},
68+
)?;
69+
70+
Ok(ExecutionOptimisticFinalizedResponse {
71+
data,
72+
execution_optimistic: Some(execution_optimistic),
73+
finalized: Some(finalized),
74+
})
75+
}
76+
77+
pub fn get_beacon_state_validator_balances<T: BeaconChainTypes>(
78+
state_id: StateId,
79+
chain: Arc<BeaconChain<T>>,
80+
optional_ids: Option<&[ValidatorId]>,
81+
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<ValidatorBalanceData>>, warp::Rejection> {
82+
let (data, execution_optimistic, finalized) = state_id
83+
.map_state_and_execution_optimistic_and_finalized(
84+
&chain,
85+
|state, execution_optimistic, finalized| {
86+
Ok((
87+
state
88+
.validators()
89+
.iter()
90+
.zip(state.balances().iter())
91+
.enumerate()
92+
// filter by validator id(s) if provided
93+
.filter(|(index, (validator, _))| {
94+
optional_ids.map_or(true, |ids| {
95+
ids.iter().any(|id| match id {
96+
ValidatorId::PublicKey(pubkey) => &validator.pubkey == pubkey,
97+
ValidatorId::Index(param_index) => {
98+
*param_index == *index as u64
99+
}
100+
})
101+
})
102+
})
103+
.map(|(index, (_, balance))| ValidatorBalanceData {
104+
index: index as u64,
105+
balance: *balance,
106+
})
107+
.collect::<Vec<_>>(),
108+
execution_optimistic,
109+
finalized,
110+
))
111+
},
112+
)?;
113+
114+
Ok(api_types::ExecutionOptimisticFinalizedResponse {
115+
data,
116+
execution_optimistic: Some(execution_optimistic),
117+
finalized: Some(finalized),
118+
})
119+
}

beacon_node/http_api/tests/tests.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,18 @@ impl ApiTester {
850850
.await
851851
.unwrap()
852852
.map(|res| res.data);
853+
let result_post_index_ids = self
854+
.client
855+
.post_beacon_states_validator_balances(state_id.0, validator_index_ids)
856+
.await
857+
.unwrap()
858+
.map(|res| res.data);
859+
let result_post_pubkey_ids = self
860+
.client
861+
.post_beacon_states_validator_balances(state_id.0, validator_pubkey_ids)
862+
.await
863+
.unwrap()
864+
.map(|res| res.data);
853865

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

869881
assert_eq!(result_index_ids, expected, "{:?}", state_id);
870882
assert_eq!(result_pubkey_ids, expected, "{:?}", state_id);
883+
assert_eq!(result_post_index_ids, expected, "{:?}", state_id);
884+
assert_eq!(result_post_pubkey_ids, expected, "{:?}", state_id);
871885
}
872886
}
873887

@@ -913,7 +927,6 @@ impl ApiTester {
913927
.await
914928
.unwrap()
915929
.map(|res| res.data);
916-
917930
let result_pubkey_ids = self
918931
.client
919932
.get_beacon_states_validators(
@@ -924,6 +937,18 @@ impl ApiTester {
924937
.await
925938
.unwrap()
926939
.map(|res| res.data);
940+
let post_result_index_ids = self
941+
.client
942+
.post_beacon_states_validators(state_id.0, Some(validator_index_ids), None)
943+
.await
944+
.unwrap()
945+
.map(|res| res.data);
946+
let post_result_pubkey_ids = self
947+
.client
948+
.post_beacon_states_validators(state_id.0, Some(validator_pubkey_ids), None)
949+
.await
950+
.unwrap()
951+
.map(|res| res.data);
927952

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

960985
assert_eq!(result_index_ids, expected, "{:?}", state_id);
961986
assert_eq!(result_pubkey_ids, expected, "{:?}", state_id);
987+
assert_eq!(post_result_index_ids, expected, "{:?}", state_id);
988+
assert_eq!(post_result_pubkey_ids, expected, "{:?}", state_id);
962989
}
963990
}
964991
}

0 commit comments

Comments
 (0)