Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/modules/checks/suites/consensus_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ def check_attestation_committees(web3: Web3, blockstamp):
assert web3.cc.get_attestation_committees(blockstamp, epoch), "consensus-client provide no attestation committees"


def check_sync_committee(web3: Web3, blockstamp):
"""Check that consensus-client able to provide sync committee"""
cc_config = web3.cc.get_config_spec()
slots_per_epoch = cc_config.SLOTS_PER_EPOCH
epoch = blockstamp.slot_number // slots_per_epoch
assert web3.cc.get_sync_committee(blockstamp, epoch), "consensus-client provide no sync committee"


def check_block_attestations_and_sync(web3: Web3, blockstamp):
"""Check that consensus-client able to provide block attestations"""
assert web3.cc.get_block_attestations_and_sync(blockstamp.slot_number), "consensus-client provide no block attestations and sync"
38 changes: 31 additions & 7 deletions src/providers/consensus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,22 @@ def get_attestation_committees(

def get_sync_committee(self, blockstamp: BlockStamp, epoch: EpochNumber) -> SyncCommittee:
"""Spec: https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochSyncCommittees"""
data, _ = self._get(
self.API_GET_SYNC_COMMITTEE,
path_params=(blockstamp.state_root,),
query_params={'epoch': epoch},
force_raise=self.__raise_on_prysm_error,
retval_validator=data_is_dict,
)
try:
data, _ = self._get(
self.API_GET_SYNC_COMMITTEE,
path_params=(blockstamp.state_root,),
query_params={'epoch': epoch},
force_raise=self.__raise_on_prysm_error,
retval_validator=data_is_dict,
)
except NotOkResponse as error:
if self.PRYSM_STATE_NOT_FOUND_ERROR in error.text:
data = self._get_sync_committee_with_prysm(
blockstamp,
epoch,
)
else:
raise error
return SyncCommittee.from_response(**data)

@list_of_dataclasses(ProposerDuties.from_response)
Expand Down Expand Up @@ -274,6 +283,21 @@ def _get_attestation_committees_with_prysm(
)
return data

def _get_sync_committee_with_prysm(
self,
blockstamp: BlockStamp,
epoch: EpochNumber,
) -> list[dict]:
# Avoid Prysm issue with state root - https://github.com/prysmaticlabs/prysm/issues/12053
# Trying to get committees by slot number
data, _ = self._get(
self.API_GET_SYNC_COMMITTEE,
path_params=(blockstamp.slot_number,),
query_params={'epoch': epoch},
retval_validator=data_is_dict,
)
return data

def __raise_last_missed_slot_error(self, errors: list[Exception]) -> Exception | None:
"""
Prioritize NotOkResponse before other exceptions (ConnectionError, TimeoutError).
Expand Down
23 changes: 23 additions & 0 deletions tests/providers/consensus/test_consensus_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from src import variables
from src.providers.consensus.client import ConsensusClient
from src.providers.consensus.types import Validator
from src.providers.http_provider import NotOkResponse
from src.types import EpochNumber, SlotNumber
from src.utils.blockstamp import build_blockstamp
from tests.factory.blockstamp import BlockStampFactory
Expand Down Expand Up @@ -67,6 +68,28 @@ def test_get_attestation_committees(consensus_client: ConsensusClient):
assert attestation_committee_by_slot[0].validators == attestation_committee.validators


@pytest.mark.integration
@pytest.mark.testnet
def test_get_sync_committee(consensus_client: ConsensusClient):
root = consensus_client.get_block_root('finalized').root
block_details = consensus_client.get_block_details(root)
blockstamp = build_blockstamp(block_details)
epoch = blockstamp.slot_number // 32

sync_committee = consensus_client.get_sync_committee(blockstamp, epoch)
assert sync_committee

# Prysm error fallback
consensus_client._get = Mock(
side_effect=[
NotOkResponse(status=404, text=consensus_client.PRYSM_STATE_NOT_FOUND_ERROR),
(sync_committee.__dict__, "dummy_metadata"),
]
)
sync_committee = consensus_client.get_sync_committee(blockstamp, epoch)
assert sync_committee


@pytest.mark.integration
@pytest.mark.testnet
def test_get_validators(consensus_client: ConsensusClient):
Expand Down