Skip to content

Commit 31ab1d3

Browse files
committed
feat: add pending_deposits and pending_partial_withdrawals accessors
1 parent b9a0d05 commit 31ab1d3

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

beacon_node/http_api/src/lib.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,81 @@ pub fn serve<T: BeaconChainTypes>(
11251125
},
11261126
);
11271127

1128+
// GET beacon/states/{state_id}/pending_deposits
1129+
let get_beacon_state_pending_deposits = beacon_states_path
1130+
.clone()
1131+
.and(warp::path("pending_deposits"))
1132+
.and(warp::path::end())
1133+
.then(
1134+
|state_id: StateId,
1135+
task_spawner: TaskSpawner<T::EthSpec>,
1136+
chain: Arc<BeaconChain<T>>| {
1137+
task_spawner.blocking_json_task(Priority::P1, move || {
1138+
let (data, execution_optimistic, finalized) = state_id
1139+
.map_state_and_execution_optimistic_and_finalized(
1140+
&chain,
1141+
|state, execution_optimistic, finalized| {
1142+
if !state.fork_name_unchecked().electra_enabled() {
1143+
return Err(warp_utils::reject::pre_electra_not_supported(
1144+
format!("state at epoch {} is not activated for Electra", state.current_epoch())
1145+
));
1146+
}
1147+
1148+
let Ok(deposits) = state.pending_deposits() else {
1149+
return Err(warp_utils::reject::custom_bad_request("Pending deposits not found".to_string()));
1150+
};
1151+
1152+
Ok((deposits.iter().cloned().collect::<Vec<_>>(), execution_optimistic, finalized))
1153+
}
1154+
)?;
1155+
1156+
Ok(api_types::ExecutionOptimisticFinalizedResponse {
1157+
data,
1158+
execution_optimistic: Some(execution_optimistic),
1159+
finalized: Some(finalized),
1160+
})
1161+
})
1162+
},
1163+
);
1164+
1165+
// GET beacon/states/{state_id}/pending_partial_withdrawals
1166+
let get_beacon_state_pending_partial_withdrawals = beacon_states_path
1167+
.clone()
1168+
.and(warp::path("pending_partial_withdrawals"))
1169+
.and(warp::path::end())
1170+
.then(
1171+
|state_id: StateId,
1172+
task_spawner: TaskSpawner<T::EthSpec>,
1173+
chain: Arc<BeaconChain<T>>| {
1174+
task_spawner.blocking_json_task(Priority::P1, move || {
1175+
let (data, execution_optimistic, finalized) = state_id
1176+
.map_state_and_execution_optimistic_and_finalized(
1177+
&chain,
1178+
|state, execution_optimistic, finalized| {
1179+
if !state.fork_name_unchecked().electra_enabled() {
1180+
return Err(warp_utils::reject::pre_electra_not_supported(
1181+
format!("state at epoch {} is not activated for Electra", state.current_epoch())
1182+
));
1183+
}
1184+
1185+
let Ok(withdrawals) = state.pending_partial_withdrawals() else {
1186+
return Err(warp_utils::reject::custom_bad_request("Pending withdrawals not found".to_string()));
1187+
};
1188+
1189+
Ok((withdrawals.iter().cloned().collect::<Vec<_>>(), execution_optimistic, finalized))
1190+
}
1191+
)?;
1192+
1193+
Ok(api_types::ExecutionOptimisticFinalizedResponse {
1194+
data,
1195+
execution_optimistic: Some(execution_optimistic),
1196+
finalized: Some(finalized),
1197+
})
1198+
})
1199+
},
1200+
);
1201+
1202+
11281203
// GET beacon/headers
11291204
//
11301205
// Note: this endpoint only returns information about blocks in the canonical chain. Given that
@@ -4673,6 +4748,8 @@ pub fn serve<T: BeaconChainTypes>(
46734748
.uor(get_beacon_state_committees)
46744749
.uor(get_beacon_state_sync_committees)
46754750
.uor(get_beacon_state_randao)
4751+
.uor(get_beacon_state_pending_deposits)
4752+
.uor(get_beacon_state_pending_partial_withdrawals)
46764753
.uor(get_beacon_headers)
46774754
.uor(get_beacon_headers_block_id)
46784755
.uor(get_beacon_block)

beacon_node/http_api/tests/tests.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,64 @@ impl ApiTester {
11921192
self
11931193
}
11941194

1195+
pub async fn test_beacon_states_pending_deposits(self) -> Self {
1196+
for state_id in self.interesting_state_ids() {
1197+
let mut state_opt = state_id
1198+
.state(&self.chain)
1199+
.ok()
1200+
.map(|(state, _execution_optimistic, _finalized)| state);
1201+
1202+
let result = self
1203+
.client
1204+
.get_beacon_states_pending_deposits(state_id.0)
1205+
.await
1206+
.unwrap()
1207+
.map(|res| res.data);
1208+
1209+
if result.is_none() && state_opt.is_none() {
1210+
continue;
1211+
}
1212+
1213+
let state = state_opt.as_mut().expect("result should be none");
1214+
let expected = state
1215+
.pending_deposits()
1216+
.unwrap();
1217+
1218+
assert_eq!(result.unwrap(), expected.to_vec());
1219+
}
1220+
1221+
self
1222+
}
1223+
1224+
pub async fn test_beacon_states_pending_partial_withdrawals(self) -> Self {
1225+
for state_id in self.interesting_state_ids() {
1226+
let mut state_opt = state_id
1227+
.state(&self.chain)
1228+
.ok()
1229+
.map(|(state, _execution_optimistic, _finalized)| state);
1230+
1231+
let result = self
1232+
.client
1233+
.get_beacon_states_pending_partial_withdrawals(state_id.0)
1234+
.await
1235+
.unwrap()
1236+
.map(|res| res.data);
1237+
1238+
if result.is_none() && state_opt.is_none() {
1239+
continue;
1240+
}
1241+
1242+
let state = state_opt.as_mut().expect("result should be none");
1243+
let expected = state
1244+
.pending_partial_withdrawals()
1245+
.unwrap();
1246+
1247+
assert_eq!(result.unwrap(), expected.to_vec());
1248+
}
1249+
1250+
self
1251+
}
1252+
11951253
pub async fn test_beacon_headers_all_slots(self) -> Self {
11961254
for slot in 0..CHAIN_LENGTH {
11971255
let slot = Slot::from(slot);
@@ -6316,6 +6374,23 @@ async fn beacon_get_state_info() {
63166374
.await;
63176375
}
63186376

6377+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
6378+
async fn beacon_get_state_info_electra() {
6379+
let mut config = ApiTesterConfig::default();
6380+
config.spec.altair_fork_epoch = Some(Epoch::new(0));
6381+
config.spec.bellatrix_fork_epoch = Some(Epoch::new(0));
6382+
config.spec.capella_fork_epoch = Some(Epoch::new(0));
6383+
config.spec.deneb_fork_epoch = Some(Epoch::new(0));
6384+
config.spec.electra_fork_epoch = Some(Epoch::new(0));
6385+
ApiTester::new_from_config(config)
6386+
.await
6387+
.test_beacon_states_pending_deposits()
6388+
.await
6389+
.test_beacon_states_pending_partial_withdrawals()
6390+
.await;
6391+
}
6392+
6393+
63196394
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
63206395
async fn beacon_get_blocks() {
63216396
ApiTester::new()

common/eth2/src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,44 @@ impl BeaconNodeHttpClient {
782782
self.get_opt(path).await
783783
}
784784

785+
/// `GET beacon/states/{state_id}/pending_deposits`
786+
///
787+
/// Returns `Ok(None)` on a 404 error.
788+
pub async fn get_beacon_states_pending_deposits(
789+
&self,
790+
state_id: StateId,
791+
) -> Result<Option<ExecutionOptimisticFinalizedResponse<Vec<PendingDeposit>>>, Error> {
792+
let mut path = self.eth_path(V1)?;
793+
794+
path.path_segments_mut()
795+
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
796+
.push("beacon")
797+
.push("states")
798+
.push(&state_id.to_string())
799+
.push("pending_deposits");
800+
801+
self.get_opt(path).await
802+
}
803+
804+
/// `GET beacon/states/{state_id}/pending_partial_withdrawals`
805+
///
806+
/// Returns `Ok(None)` on a 404 error.
807+
pub async fn get_beacon_states_pending_partial_withdrawals(
808+
&self,
809+
state_id: StateId,
810+
) -> Result<Option<ExecutionOptimisticFinalizedResponse<Vec<PendingPartialWithdrawal>>>, Error> {
811+
let mut path = self.eth_path(V1)?;
812+
813+
path.path_segments_mut()
814+
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
815+
.push("beacon")
816+
.push("states")
817+
.push(&state_id.to_string())
818+
.push("pending_partial_withdrawals");
819+
820+
self.get_opt(path).await
821+
}
822+
785823
/// `GET beacon/light_client/updates`
786824
///
787825
/// Returns `Ok(None)` on a 404 error.

0 commit comments

Comments
 (0)