Skip to content

Commit 6953e11

Browse files
committed
refactor: use explicit validator for http provider
1 parent 7262c99 commit 6953e11

File tree

5 files changed

+113
-175
lines changed

5 files changed

+113
-175
lines changed

src/providers/consensus/client.py

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Literal, cast
2+
from typing import Any, Literal, cast
33

44
from src import variables
55
from src.metrics.logging import logging
@@ -20,7 +20,13 @@
2020
SyncCommittee,
2121
Validator,
2222
)
23-
from src.providers.http_provider import HTTPProvider, NotOkResponse
23+
from src.providers.http_provider import (
24+
HTTPProvider,
25+
NotOkResponse,
26+
data_is_dict,
27+
data_is_list,
28+
data_is_transient_dict,
29+
)
2430
from src.types import BlockRoot, BlockStamp, EpochNumber, SlotNumber, StateRoot
2531
from src.utils.cache import global_lru_cache as lru_cache
2632
from src.utils.dataclass import list_of_dataclasses
@@ -59,14 +65,14 @@ class ConsensusClient(HTTPProvider):
5965

6066
def get_config_spec(self) -> BeaconSpecResponse:
6167
"""Spec: https://ethereum.github.io/beacon-APIs/#/Config/getSpec"""
62-
data, _ = self._get(self.API_GET_SPEC, is_dict=True)
68+
data, _ = self._get(self.API_GET_SPEC, retval_validator=data_is_dict)
6369
return BeaconSpecResponse.from_response(**data)
6470

6571
def get_genesis(self):
6672
"""
6773
Spec: https://ethereum.github.io/beacon-APIs/#/Beacon/getGenesis
6874
"""
69-
data, _ = self._get(self.API_GET_GENESIS, is_dict=True)
75+
data, _ = self._get(self.API_GET_GENESIS, retval_validator=data_is_dict)
7076
return GenesisResponse.from_response(**data)
7177

7278
def get_block_root(self, state_id: SlotNumber | BlockRoot | LiteralState) -> BlockRootResponse:
@@ -79,7 +85,7 @@ def get_block_root(self, state_id: SlotNumber | BlockRoot | LiteralState) -> Blo
7985
self.API_GET_BLOCK_ROOT,
8086
path_params=(state_id,),
8187
force_raise=self.__raise_last_missed_slot_error,
82-
is_dict=True,
88+
retval_validator=data_is_dict,
8389
)
8490
return BlockRootResponse.from_response(**data)
8591

@@ -90,7 +96,7 @@ def get_block_header(self, state_id: SlotNumber | BlockRoot) -> BlockHeaderFullR
9096
self.API_GET_BLOCK_HEADER,
9197
path_params=(state_id,),
9298
force_raise=self.__raise_last_missed_slot_error,
93-
is_dict=True,
99+
retval_validator=data_is_dict,
94100
)
95101
resp = BlockHeaderFullResponse.from_response(data=BlockHeaderResponseData.from_response(**data), **meta_data)
96102
return resp
@@ -102,7 +108,7 @@ def get_block_details(self, state_id: SlotNumber | BlockRoot) -> BlockDetailsRes
102108
self.API_GET_BLOCK_DETAILS,
103109
path_params=(state_id,),
104110
force_raise=self.__raise_last_missed_slot_error,
105-
is_dict=True,
111+
retval_validator=data_is_dict,
106112
)
107113
return BlockDetailsResponse.from_response(**data)
108114

@@ -115,7 +121,7 @@ def get_block_attestations_and_sync(
115121
self.API_GET_BLOCK_DETAILS,
116122
path_params=(state_id,),
117123
force_raise=self.__raise_last_missed_slot_error,
118-
is_dict=True,
124+
retval_validator=data_is_dict,
119125
)
120126

121127
attestations = [
@@ -141,7 +147,7 @@ def get_attestation_committees(
141147
path_params=(blockstamp.state_root,),
142148
query_params={'epoch': epoch, 'index': committee_index, 'slot': slot},
143149
force_raise=self.__raise_on_prysm_error,
144-
is_list=True,
150+
retval_validator=data_is_list,
145151
)
146152
except NotOkResponse as error:
147153
if self.PRYSM_STATE_NOT_FOUND_ERROR in error.text:
@@ -162,36 +168,40 @@ def get_sync_committee(self, blockstamp: BlockStamp, epoch: EpochNumber) -> Sync
162168
path_params=(blockstamp.state_root,),
163169
query_params={'epoch': epoch},
164170
force_raise=self.__raise_on_prysm_error,
165-
is_dict=True,
171+
retval_validator=data_is_dict,
166172
)
167173
return SyncCommittee.from_response(**data)
168174

169175
@list_of_dataclasses(ProposerDuties.from_response)
170176
def get_proposer_duties(self, epoch: EpochNumber, expected_dependent_root: BlockRoot) -> list[ProposerDuties]:
171177
"""Spec: https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties"""
172-
# It is recommended by spec to use the dependent root to ensure the epoch is correct
173-
proposer_data, proposer_meta = self._get(
178+
179+
def data_is_list_and_dependent_root_matches(data: Any, meta: dict, endpoint: str):
180+
data_is_list(data, meta, endpoint=endpoint)
181+
# It is recommended by spec to use the dependent root to ensure the epoch is correct
182+
if meta["dependent_root"] != expected_dependent_root:
183+
raise ValueError(
184+
"Dependent root for proposer duties request mismatch: "
185+
f"{meta['dependent_root']=} is not {expected_dependent_root=}. "
186+
"Probably, CL node is not fully synced."
187+
)
188+
189+
data, _ = self._get(
174190
self.API_GET_PROPOSER_DUTIES,
175191
path_params=(epoch,),
176-
is_list=True,
192+
retval_validator=data_is_list_and_dependent_root_matches,
177193
)
178-
response_dependent_root = proposer_meta['dependent_root']
179-
if response_dependent_root != expected_dependent_root:
180-
raise ValueError(
181-
"Dependent root for proposer duties request mismatch: "
182-
f"{response_dependent_root=} is not {expected_dependent_root=}. Probably, CL node is not fully synced"
183-
)
184-
return proposer_data
194+
return data
185195

186196
@lru_cache(maxsize=1)
187197
def get_state_block_roots(self, state_id: SlotNumber) -> list[BlockRoot]:
188-
streamed_json = self._get(
198+
data, _ = self._get(
189199
self.API_GET_STATE,
190200
path_params=(state_id,),
191201
stream=True,
192-
is_dict=True,
202+
retval_validator=data_is_transient_dict,
193203
)
194-
return list(streamed_json['data']['block_roots'])
204+
return list(data["block_roots"])
195205

196206
def get_validators(self, blockstamp: BlockStamp) -> list[Validator]:
197207
return self.get_state_view(blockstamp).indexed_validators
@@ -232,7 +242,7 @@ def _get_state_by_state_id(self, state_id: StateRoot | SlotNumber) -> dict:
232242
self.API_GET_STATE,
233243
path_params=(state_id,),
234244
force_raise=self.__raise_on_prysm_error,
235-
is_dict=True,
245+
retval_validator=data_is_dict,
236246
)
237247
return data
238248

@@ -260,7 +270,7 @@ def _get_attestation_committees_with_prysm(
260270
self.API_GET_ATTESTATION_COMMITTEES,
261271
path_params=(blockstamp.slot_number,),
262272
query_params={'epoch': epoch, 'index': index, 'slot': slot},
263-
is_list=True,
273+
retval_validator=data_is_dict,
264274
)
265275
return data
266276

@@ -280,6 +290,6 @@ def _get_chain_id_with_provider(self, provider_index: int) -> int:
280290
data, _ = self._get_without_fallbacks(
281291
self.hosts[provider_index],
282292
self.API_GET_SPEC,
283-
is_dict=True,
293+
retval_validator=data_is_dict,
284294
)
285295
return BeaconSpecResponse.from_response(**data).DEPOSIT_CHAIN_ID

0 commit comments

Comments
 (0)