From 9e21be09d8f4c2356228284ffb52f4f6a3026e35 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Sun, 19 May 2024 00:41:44 +0800 Subject: [PATCH] Support blob_sidecars beacon endpoint Co-authored-by: Felipe Selmo --- docs/web3.beacon.rst | 45 +++++++++++++++++++++++++++++++ newsfragments/3407.feature.rst | 1 + tests/beacon/test_async_beacon.py | 13 +++++++++ tests/beacon/test_beacon.py | 12 +++++++++ web3/beacon/api_endpoints.py | 3 +++ web3/beacon/async_beacon.py | 20 ++++++++++++-- web3/beacon/beacon.py | 20 ++++++++++++-- 7 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 newsfragments/3407.feature.rst diff --git a/docs/web3.beacon.rst b/docs/web3.beacon.rst index 15624b80f8..6520df8e42 100644 --- a/docs/web3.beacon.rst +++ b/docs/web3.beacon.rst @@ -432,6 +432,51 @@ Methods ] } +.. py:method:: Beacon.get_blob_sidecars(block_id, indices=[]) + + .. code-block:: python + + >>> beacon.get_blob_sidecars(1, indices=[1]) + { + "data": [ + { + "index": "1", + "blob": ..., # ommitted + "kzg_commitment": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "kzg_proof": "0x7FB0A12D11Ffe8A48c2fF80dCA17adbCC1da5F6aADaAEF2b338717dcDEECf6DaB9FD7C4e4265CfBc097cD31dCB19E836", + "signed_block_header": { + "message": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "kzg_commitment_inclusion_proof": [ + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + ] + } + ] + } + .. py:method:: Beacon.get_node_identity() .. code-block:: python diff --git a/newsfragments/3407.feature.rst b/newsfragments/3407.feature.rst new file mode 100644 index 0000000000..db841cefc5 --- /dev/null +++ b/newsfragments/3407.feature.rst @@ -0,0 +1 @@ +Add sync and async support for beacon ``/eth/v1/beacon/blob_sidecars`` endpoint. diff --git a/tests/beacon/test_async_beacon.py b/tests/beacon/test_async_beacon.py index ca0efa9509..945629b382 100644 --- a/tests/beacon/test_async_beacon.py +++ b/tests/beacon/test_async_beacon.py @@ -267,3 +267,16 @@ async def test_async_cl_node_get_version(async_beacon): async def test_async_cl_node_get_syncing(async_beacon): response = await async_beacon.get_syncing() _assert_valid_response(response) + + +# Blob endpoint tests + + +@pytest.mark.asyncio +async def test_async_cl_node_get_blob_sidecars(async_beacon): + response = await async_beacon.get_blob_sidecars("head") + _assert_valid_response(response) + + # test with indices + with_indices = await async_beacon.get_blob_sidecars("head", [0, 1]) + _assert_valid_response(with_indices) diff --git a/tests/beacon/test_beacon.py b/tests/beacon/test_beacon.py index 112c2276f2..63d71fcafd 100644 --- a/tests/beacon/test_beacon.py +++ b/tests/beacon/test_beacon.py @@ -223,3 +223,15 @@ def test_cl_node_get_version(beacon): def test_cl_node_get_syncing(beacon): response = beacon.get_syncing() _assert_valid_response(response) + + +# Blob endpoint tests + + +def test_cl_node_get_blob_sidecars(beacon): + response = beacon.get_blob_sidecars("head") + _assert_valid_response(response) + + # test with indices + with_indices = beacon.get_blob_sidecars("head", [0, 1]) + _assert_valid_response(with_indices) diff --git a/web3/beacon/api_endpoints.py b/web3/beacon/api_endpoints.py index 570f8218c0..4cd1a52617 100644 --- a/web3/beacon/api_endpoints.py +++ b/web3/beacon/api_endpoints.py @@ -26,6 +26,9 @@ # rewards GET_REWARDS = "/eth/v1/beacon/rewards/blocks/{0}" +# blobs +GET_BLOB_SIDECARS = "/eth/v1/beacon/blob_sidecars/{0}" + # light client GET_LIGHT_CLIENT_BOOTSTRAP_STRUCTURE = "/eth/v1/beacon/light_client/bootstrap/{0}" GET_LIGHT_CLIENT_UPDATES = "/eth/v1/beacon/light_client/updates" diff --git a/web3/beacon/async_beacon.py b/web3/beacon/async_beacon.py index 5368caffc3..7577b3ad56 100644 --- a/web3/beacon/async_beacon.py +++ b/web3/beacon/async_beacon.py @@ -1,6 +1,8 @@ from typing import ( Any, Dict, + List, + Optional, ) from eth_typing import ( @@ -17,6 +19,7 @@ GET_BEACON_HEADS, GET_BEACON_STATE, GET_BLINDED_BLOCKS, + GET_BLOB_SIDECARS, GET_BLOCK, GET_BLOCK_ATTESTATIONS, GET_BLOCK_HEADER, @@ -64,10 +67,12 @@ def __init__( self.request_timeout = request_timeout self._request_session_manager = HTTPSessionManager() - async def _async_make_get_request(self, endpoint_uri: str) -> Dict[str, Any]: + async def _async_make_get_request( + self, endpoint_uri: str, params: Optional[Dict[str, str]] = None + ) -> Dict[str, Any]: uri = URI(self.base_url + endpoint_uri) return await self._request_session_manager.async_json_make_get_request( - uri, timeout=self.request_timeout + uri, params=params, timeout=self.request_timeout ) # [ BEACON endpoints ] @@ -220,3 +225,14 @@ async def get_version(self) -> Dict[str, Any]: async def get_syncing(self) -> Dict[str, Any]: return await self._async_make_get_request(GET_SYNCING) + + # [ BLOB endpoints ] + + async def get_blob_sidecars( + self, block_id: str, indices: Optional[List[int]] = None + ) -> Dict[str, Any]: + indices_param = {"indices": ",".join(map(str, indices))} if indices else None + return await self._async_make_get_request( + GET_BLOB_SIDECARS.format(block_id), + params=indices_param, + ) diff --git a/web3/beacon/beacon.py b/web3/beacon/beacon.py index 2a77585159..4553a8b800 100644 --- a/web3/beacon/beacon.py +++ b/web3/beacon/beacon.py @@ -1,6 +1,8 @@ from typing import ( Any, Dict, + List, + Optional, ) from eth_typing import ( @@ -17,6 +19,7 @@ GET_BEACON_HEADS, GET_BEACON_STATE, GET_BLINDED_BLOCKS, + GET_BLOB_SIDECARS, GET_BLOCK, GET_BLOCK_ATTESTATIONS, GET_BLOCK_HEADER, @@ -62,10 +65,12 @@ def __init__( self.request_timeout = request_timeout self._request_session_manager = HTTPSessionManager() - def _make_get_request(self, endpoint_url: str) -> Dict[str, Any]: + def _make_get_request( + self, endpoint_url: str, params: Optional[Dict[str, str]] = None + ) -> Dict[str, Any]: uri = URI(self.base_url + endpoint_url) return self._request_session_manager.json_make_get_request( - uri, timeout=self.request_timeout + uri, params=params, timeout=self.request_timeout ) # [ BEACON endpoints ] @@ -206,3 +211,14 @@ def get_version(self) -> Dict[str, Any]: def get_syncing(self) -> Dict[str, Any]: return self._make_get_request(GET_SYNCING) + + # [ BLOB endpoints ] + + def get_blob_sidecars( + self, block_id: str, indices: Optional[List[int]] = None + ) -> Dict[str, Any]: + indices_param = {"indices": ",".join(map(str, indices))} if indices else None + return self._make_get_request( + GET_BLOB_SIDECARS.format(block_id), + params=indices_param, + )