From 70fcefd9cd1d85476570ee133fa4276d6b5852c2 Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 27 Sep 2024 16:59:06 -0400 Subject: [PATCH 01/19] fix: collector cloning in build.sh --- build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sh b/build.sh index bcea918..8199bdc 100755 --- a/build.sh +++ b/build.sh @@ -166,6 +166,7 @@ ARG1=${1:-yes_collector} if [ "$DEVMODE" = "true" ]; then echo "Building local collector..." + rm -rf snapshotter-lite-local-collector git clone https://github.com/powerloom/snapshotter-lite-local-collector.git --single-branch --branch main (cd ./snapshotter-lite-local-collector/ && chmod +x build-docker.sh && ./build-docker.sh) From c1b1c3cc7853b62e4e9c71273e511578a4a52c5e Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 27 Sep 2024 18:24:51 -0400 Subject: [PATCH 02/19] chore: update last finalized core api endpoint to not loop --- snapshotter/core_api.py | 34 +++++++++++----------------------- snapshotter/utils/rpc.py | 9 ++++++++- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/snapshotter/core_api.py b/snapshotter/core_api.py index 54f32f4..21c42c9 100644 --- a/snapshotter/core_api.py +++ b/snapshotter/core_api.py @@ -200,33 +200,21 @@ async def get_project_last_finalized_epoch_info( try: # Find the last finalized epoch from the contract - epoch_finalized = False - [cur_epoch] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.currentEpoch(Web3.to_checksum_address(settings.data_market))], + [project_last_finalized_epoch] = await request.app.state.anchor_rpc_helper.web3_call( + tasks=[ + ('lastFinalizedSnapshot', [Web3.to_checksum_address(settings.data_market), project_id]), + ], + contract_addr=protocol_state_contract_address, + abi=protocol_state_contract_abi, ) - epoch_id = int(cur_epoch[2]) - - # Iterate backwards through epochs until a finalized one is found - while not epoch_finalized and epoch_id >= 0: - # Get finalization status - [epoch_finalized_contract] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.snapshotStatus(settings.data_market, project_id, epoch_id)], - ) - if epoch_finalized_contract[0]: - epoch_finalized = True - project_last_finalized_epoch = epoch_id - else: - epoch_id -= 1 - if epoch_id < 0: - response.status_code = 404 - return { - 'status': 'error', - 'message': f'Unable to find last finalized epoch for project {project_id}', - } # Get epoch info for the last finalized epoch [epoch_info_data] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.epochInfo(Web3.to_checksum_address(settings.data_market), project_last_finalized_epoch)], + tasks=[ + ('epochInfo', [Web3.to_checksum_address(settings.data_market), project_last_finalized_epoch]), + ], + contract_addr=protocol_state_contract_address, + abi=protocol_state_contract_abi, ) epoch_info = { 'epochId': project_last_finalized_epoch, diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index 937044b..a8189b4 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -460,8 +460,15 @@ async def f(node_idx): response = await asyncio.gather(*web3_tasks) return response except Exception as e: + # Create a serializable version of the tasks + serializable_tasks = [ + { + "function_name": task[0], + "args": task[1] + } for task in tasks + ] exc = RPCException( - request=tasks, + request=serializable_tasks, response=None, underlying_exception=e, extra_info={'msg': str(e)}, From 72f56362f98c56b57ca29c018b56c5b6e7e85f81 Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 27 Sep 2024 18:25:22 -0400 Subject: [PATCH 03/19] chore: pre-commit formatting --- snapshotter/core_api.py | 23 +++++++++++++++++------ snapshotter/utils/rpc.py | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/snapshotter/core_api.py b/snapshotter/core_api.py index 21c42c9..5a0f572 100644 --- a/snapshotter/core_api.py +++ b/snapshotter/core_api.py @@ -4,7 +4,6 @@ It includes functionality for health checks, epoch information retrieval, project data fetching, and task status checking. """ - from fastapi import FastAPI from fastapi import Request from fastapi import Response @@ -117,7 +116,11 @@ async def get_current_epoch( """ try: [current_epoch_data] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.currentEpoch(Web3.to_checksum_address(settings.data_market))], + [ + request.app.state.protocol_state_contract.functions.currentEpoch( + Web3.to_checksum_address(settings.data_market), + ), + ], ) current_epoch = { 'begin': current_epoch_data[0], @@ -158,7 +161,11 @@ async def get_epoch_info( """ try: [epoch_info_data] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.epochInfo(Web3.to_checksum_address(settings.data_market), epoch_id)], + [ + request.app.state.protocol_state_contract.functions.epochInfo( + Web3.to_checksum_address(settings.data_market), epoch_id, + ), + ], ) epoch_info = { 'timestamp': epoch_info_data[0], @@ -207,7 +214,7 @@ async def get_project_last_finalized_epoch_info( contract_addr=protocol_state_contract_address, abi=protocol_state_contract_abi, ) - + # Get epoch info for the last finalized epoch [epoch_info_data] = await request.app.state.anchor_rpc_helper.web3_call( tasks=[ @@ -372,11 +379,15 @@ async def get_task_status_post( # Construct project ID project_id = f'{task_status_request.task_type}:{task_status_request.wallet_address.lower()}:{settings.namespace}' - + try: # Get the last finalized epoch for the project [last_finalized_epoch] = await request.app.state.anchor_rpc_helper.web3_call( - [request.app.state.protocol_state_contract.functions.lastFinalizedSnapshot(Web3.to_checksum_address(settings.data_market), project_id)], + [ + request.app.state.protocol_state_contract.functions.lastFinalizedSnapshot( + Web3.to_checksum_address(settings.data_market), project_id, + ), + ], ) except Exception as e: diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index a8189b4..960c61c 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -463,8 +463,8 @@ async def f(node_idx): # Create a serializable version of the tasks serializable_tasks = [ { - "function_name": task[0], - "args": task[1] + 'function_name': task[0], + 'args': task[1], } for task in tasks ] exc = RPCException( From 080b2f6f0f8bb523b9b23aaa414461379aff9824 Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 27 Sep 2024 19:58:23 -0400 Subject: [PATCH 04/19] chore: update all core-api endpoints for contract/data util changes --- snapshotter/core_api.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/snapshotter/core_api.py b/snapshotter/core_api.py index 5a0f572..be5936d 100644 --- a/snapshotter/core_api.py +++ b/snapshotter/core_api.py @@ -20,6 +20,7 @@ from snapshotter.utils.default_logger import logger from snapshotter.utils.file_utils import read_json_file from snapshotter.utils.models.data_models import TaskStatusRequest +from snapshotter.utils.redis.redis_conn import RedisPoolCache from snapshotter.utils.rpc import RpcHelper @@ -79,6 +80,9 @@ async def startup_boilerplate(): await app.state.ipfs_singleton.init_sessions() app.state.ipfs_reader_client = app.state.ipfs_singleton._ipfs_read_client app.state.epoch_size = 0 + app.state._aioredis_pool = RedisPoolCache() + await app.state._aioredis_pool.populate() + app.state.redis_conn = app.state._aioredis_pool._aioredis_pool @app.get('/health') @@ -116,11 +120,11 @@ async def get_current_epoch( """ try: [current_epoch_data] = await request.app.state.anchor_rpc_helper.web3_call( - [ - request.app.state.protocol_state_contract.functions.currentEpoch( - Web3.to_checksum_address(settings.data_market), - ), + tasks=[ + ('currentEpoch', [Web3.to_checksum_address(settings.data_market)]), ], + contract_addr=protocol_state_contract_address, + abi=protocol_state_contract_abi, ) current_epoch = { 'begin': current_epoch_data[0], @@ -161,11 +165,11 @@ async def get_epoch_info( """ try: [epoch_info_data] = await request.app.state.anchor_rpc_helper.web3_call( - [ - request.app.state.protocol_state_contract.functions.epochInfo( - Web3.to_checksum_address(settings.data_market), epoch_id, - ), + tasks=[ + ('epochInfo', [Web3.to_checksum_address(settings.data_market), epoch_id]), ], + contract_addr=protocol_state_contract_address, + abi=protocol_state_contract_abi, ) epoch_info = { 'timestamp': epoch_info_data[0], @@ -271,6 +275,7 @@ async def get_data_for_project_id_epoch_id( } try: data = await get_project_epoch_snapshot( + request.app.state.redis_conn, request.app.state.protocol_state_contract, request.app.state.anchor_rpc_helper, request.app.state.ipfs_reader_client, @@ -321,8 +326,8 @@ async def get_finalized_cid_for_project_id_epoch_id( try: data = await get_project_finalized_cid( + request.app.state.redis_conn, request.app.state.protocol_state_contract, - settings.data_market, request.app.state.anchor_rpc_helper, epoch_id, project_id, @@ -383,11 +388,11 @@ async def get_task_status_post( try: # Get the last finalized epoch for the project [last_finalized_epoch] = await request.app.state.anchor_rpc_helper.web3_call( - [ - request.app.state.protocol_state_contract.functions.lastFinalizedSnapshot( - Web3.to_checksum_address(settings.data_market), project_id, - ), + tasks=[ + ('lastFinalizedSnapshot', [Web3.to_checksum_address(settings.data_market), project_id]), ], + contract_addr=protocol_state_contract_address, + abi=protocol_state_contract_abi, ) except Exception as e: From c5c9de8d2099a9bf87c5480756700113b8b5467d Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 13:19:33 -0400 Subject: [PATCH 05/19] chore: initial implementation for chain rpc rate limiting --- snapshotter/utils/models/settings_model.py | 8 ++++++ snapshotter/utils/rpc.py | 31 ++++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/snapshotter/utils/models/settings_model.py b/snapshotter/utils/models/settings_model.py index b80d70d..9201704 100644 --- a/snapshotter/utils/models/settings_model.py +++ b/snapshotter/utils/models/settings_model.py @@ -34,6 +34,13 @@ class ConnectionLimits(BaseModel): keepalive_expiry: int = 300 +class RateLimitConfig(BaseModel): + """RPC Rate limit configuration model.""" + requests_per_second: int + requests_per_minute: int + requests_per_day: int + + class RPCConfigBase(BaseModel): """Base RPC configuration model.""" full_nodes: List[RPCNodeConfig] @@ -49,6 +56,7 @@ class RPCConfigFull(RPCConfigBase): skip_epoch_threshold_blocks: int polling_interval: int semaphore_value: int = 20 + rate_limit: RateLimitConfig class RLimit(BaseModel): diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index 960c61c..ecf94b3 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -5,6 +5,7 @@ import eth_abi import tenacity +from aiolimiter import AsyncLimiter from eth_abi.codec import ABICodec from eth_utils import keccak from hexbytes import HexBytes @@ -151,6 +152,7 @@ def __init__(self, rpc_settings: RPCConfigBase = settings.rpc, archive_mode=Fals self._client = None self._async_transport = None self._semaphore = None + self._rate_limiter = None async def _init_http_clients(self): """ @@ -201,6 +203,11 @@ async def init(self, redis_conn=None): """ if not self._initialized: self._semaphore = asyncio.BoundedSemaphore(value=settings.rpc.semaphore_value) + + # Initialize rate limiter + requests_per_second = self._rpc_settings.rate_limit.requests_per_second + self._rate_limiter = AsyncLimiter(requests_per_second) + if not self._sync_nodes_initialized: self._logger.debug('Sync nodes not initialized, initializing...') self.sync_init() @@ -298,6 +305,10 @@ def _on_node_exception(self, retry_state: tenacity.RetryCallState): next_node_idx, retry_state.outcome.exception(), ) + async def _rate_limited_call(self, coroutine): + async with self._rate_limiter: + return await coroutine + @acquire_rpc_semaphore async def get_current_block_number(self, redis_conn=None): """ @@ -324,7 +335,7 @@ async def f(node_idx): web3_provider = node['web3_client_async'] try: - current_block = await web3_provider.eth.block_number + current_block = await self._rate_limited_call(web3_provider.eth.block_number) except Exception as e: exc = RPCException( request='get_current_block_number', @@ -365,8 +376,8 @@ async def f(node_idx): node = self._nodes[node_idx] try: - tx_receipt_details = await node['web3_client_async'].eth.get_transaction_receipt( - tx_hash, + tx_receipt_details = await self._rate_limited_call( + node['web3_client_async'].eth.get_transaction_receipt(tx_hash), ) except Exception as e: exc = RPCException( @@ -410,7 +421,7 @@ async def f(node_idx): web3_provider = node['web3_client_async'] try: - current_block = await web3_provider.eth.block_number + current_block = await self._rate_limited_call(web3_provider.eth.block_number) except Exception as e: exc = RPCException( request='get_current_block_number', @@ -455,7 +466,9 @@ async def f(node_idx): abi=abi, ) web3_tasks = [ - contract_obj.functions[task[0]](*task[1]).call() for task in tasks + self._rate_limited_call( + contract_obj.functions[task[0]](*task[1]).call(), + ) for task in tasks ] response = await asyncio.gather(*web3_tasks) return response @@ -509,7 +522,9 @@ async def f(node_idx): node = self._nodes[node_idx] rpc_url = node.get('rpc_url') try: - response = await self._client.post(url=rpc_url, json=rpc_query) + response = await self._rate_limited_call( + self._client.post(url=rpc_url, json=rpc_query), + ) response_data = response.json() except Exception as e: exc = RPCException( @@ -827,8 +842,8 @@ async def f(node_idx): 'topics': topics, } try: - event_log = await web3_provider.eth.get_logs( - event_log_query, + event_log = await self._rate_limited_call( + web3_provider.eth.get_logs(event_log_query), ) codec: ABICodec = web3_provider.codec all_events = [] From 781433fbd87ff344d0f268e7a703ff1c22b44a6b Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 16:12:47 -0400 Subject: [PATCH 06/19] chore: remove deprecated redis_conn argument from rpc functions --- snapshotter/utils/rpc.py | 46 ++++++++++++---------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index ecf94b3..6a021c7 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -189,15 +189,12 @@ async def _load_async_web3_providers(self): self._logger.info('Loaded async web3 provider for node {}: {}', node['rpc_url'], node['web3_client_async']) self._logger.info('Post async web3 provider loading: {}', self._nodes) - async def init(self, redis_conn=None): + async def init(self): """ Initializes the RPC client by loading web3 providers and rate limits, loading rate limit SHAs, initializing HTTP clients, and loading async web3 providers. - Args: - redis_conn: Redis connection object. - Returns: None """ @@ -310,13 +307,8 @@ async def _rate_limited_call(self, coroutine): return await coroutine @acquire_rpc_semaphore - async def get_current_block_number(self, redis_conn=None): + async def get_current_block_number(self): """ - Returns the current block number of the Ethereum blockchain. - - Args: - redis_conn: Redis connection object. - Returns: int: The current block number of the Ethereum blockchain. @@ -350,13 +342,12 @@ async def f(node_idx): return await f(node_idx=0) @acquire_rpc_semaphore - async def get_transaction_receipt(self, tx_hash, redis_conn=None): + async def get_transaction_receipt(self, tx_hash): """ Retrieves the transaction receipt for a given transaction hash. Args: tx_hash (str): The transaction hash for which to retrieve the receipt. - redis_conn: Redis connection object. Returns: dict: The transaction receipt details as a dictionary. @@ -395,12 +386,11 @@ async def f(node_idx): return await f(node_idx=0) @acquire_rpc_semaphore - async def get_current_block(self, redis_conn=None, node_idx=0): + async def get_current_block(self, node_idx=0): """ Returns the current block number of the Ethereum blockchain. Args: - redis_conn: Redis connection object. node_idx (int): Index of the node to use for the RPC call. Returns: @@ -496,13 +486,12 @@ async def f(node_idx): return await f(node_idx=0) @acquire_rpc_semaphore - async def _make_rpc_jsonrpc_call(self, rpc_query, redis_conn=None): + async def _make_rpc_jsonrpc_call(self, rpc_query): """ Makes an RPC JSON-RPC call to a node in the pool. Args: rpc_query (dict): The JSON-RPC query to be sent. - redis_conn (Redis): The Redis connection object. Returns: dict: The JSON-RPC response data. @@ -584,7 +573,6 @@ async def f(node_idx): async def batch_eth_get_balance_on_block_range( self, address, - redis_conn, from_block, to_block, ): @@ -593,7 +581,6 @@ async def batch_eth_get_balance_on_block_range( Args: address (str): The Ethereum address to retrieve the balance for. - redis_conn (redis.Redis): The Redis connection object. from_block (int): The starting block number. to_block (int): The ending block number. @@ -615,7 +602,7 @@ async def batch_eth_get_balance_on_block_range( request_id += 1 try: - response_data = await self._make_rpc_jsonrpc_call(rpc_query, redis_conn) + response_data = await self._make_rpc_jsonrpc_call(rpc_query) rpc_response = [] if not isinstance(response_data, list) and response_data is not None and isinstance(response_data, dict): @@ -635,7 +622,6 @@ async def batch_eth_call_on_block_range( abi_dict, function_name, contract_address, - redis_conn, from_block, to_block, params: Union[List, None] = None, @@ -648,7 +634,6 @@ async def batch_eth_call_on_block_range( abi_dict (dict): The ABI dictionary of the contract. function_name (str): The name of the function to call. contract_address (str): The address of the contract. - redis_conn (redis.Redis): The Redis connection object. from_block (int): The starting block number. to_block (int): The ending block number. params (list, optional): The list of parameters to pass to the function. Defaults to None. @@ -683,7 +668,7 @@ async def batch_eth_call_on_block_range( ) request_id += 1 - response_data = await self._make_rpc_jsonrpc_call(rpc_query, redis_conn=redis_conn) + response_data = await self._make_rpc_jsonrpc_call(rpc_query) rpc_response = [] if isinstance(response_data, list): response = response_data @@ -707,7 +692,6 @@ async def batch_eth_call_on_block_range_hex_data( abi_dict, function_name, contract_address, - redis_conn, from_block, to_block, params: Union[List, None] = None, @@ -720,7 +704,6 @@ async def batch_eth_call_on_block_range_hex_data( abi_dict (dict): The ABI dictionary of the contract. function_name (str): The name of the function to call. contract_address (str): The address of the contract. - redis_conn (redis.Redis): The Redis connection object. from_block (int): The starting block number. to_block (int): The ending block number. params (list, optional): The list of parameters to pass to the function. Defaults to None. @@ -755,7 +738,7 @@ async def batch_eth_call_on_block_range_hex_data( ) request_id += 1 - response_data = await self._make_rpc_jsonrpc_call(rpc_query, redis_conn=redis_conn) + response_data = await self._make_rpc_jsonrpc_call(rpc_query) rpc_response = [] # Return the hexbytes data to be decoded outside the function @@ -769,14 +752,13 @@ async def batch_eth_call_on_block_range_hex_data( return rpc_response - async def batch_eth_get_block(self, from_block, to_block, redis_conn): + async def batch_eth_get_block(self, from_block, to_block): """ Batch retrieves Ethereum blocks using eth_getBlockByNumber JSON-RPC method. Args: from_block (int): The block number to start retrieving from. to_block (int): The block number to stop retrieving at. - redis_conn (redis.Redis): Redis connection object. Returns: dict: A dictionary containing the response data from the JSON-RPC call. @@ -798,12 +780,12 @@ async def batch_eth_get_block(self, from_block, to_block, redis_conn): ) request_id += 1 - response_data = await self._make_rpc_jsonrpc_call(rpc_query, redis_conn=redis_conn) + response_data = await self._make_rpc_jsonrpc_call(rpc_query) return response_data @acquire_rpc_semaphore async def get_events_logs( - self, contract_address, to_block, from_block, topics, event_abi, redis_conn=None, + self, contract_address, to_block, from_block, topics, event_abi, ): """ Returns all events logs for a given contract address, within a specified block range and with specified topics. @@ -814,7 +796,6 @@ async def get_events_logs( from_block (int): The lowest block number to retrieve events logs from. topics (List[str]): A list of topics to filter the events logs by. event_abi (Dict): The ABI of the event to decode the logs with. - redis_conn (Redis): The Redis connection object to use for rate limiting. Returns: List[Dict]: A list of dictionaries representing the decoded events logs. @@ -870,17 +851,16 @@ async def f(node_idx): return await f(node_idx=0) - async def eth_get_block(self, redis_conn=None, block_number=None): + async def eth_get_block(self, block_number=None): """ Batch retrieves Ethereum blocks using eth_getBlockByNumber JSON-RPC method. Args: block_number (int): The block number to retrieve. - redis_conn (redis.Redis): Redis connection object. Returns: JSON-RPC response: A response containing the block data from the JSON-RPC call to fetch the respective block. """ if not self._initialized: - await self.init(redis_conn) + await self.init() rpc_query = [] block = hex(block_number) if block_number is not None else 'latest' From 4a41cec1c3d806ec237c2fb3bd94c6a19011fa2d Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 16:19:52 -0400 Subject: [PATCH 07/19] chore: remove redis_conn argument from rpc_helper calls --- snapshotter/processor_distributor.py | 4 ++-- snapshotter/snapshotter_id_ping.py | 2 +- snapshotter/system_event_detector.py | 6 +++--- snapshotter/tests/test_web3_async_provider.py | 4 ++-- snapshotter/utils/generic_worker.py | 4 ++-- .../preloaders/tx_receipts/delegated_worker/tx_receipts.py | 3 +-- snapshotter/utils/snapshot_utils.py | 2 +- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/snapshotter/processor_distributor.py b/snapshotter/processor_distributor.py index 13a6a3d..877a92d 100644 --- a/snapshotter/processor_distributor.py +++ b/snapshotter/processor_distributor.py @@ -184,9 +184,9 @@ async def _init_rpc_helper(self): Initializes the RpcHelper instance if it is not already initialized. """ self._rpc_helper = RpcHelper() - await self._rpc_helper.init(redis_conn=self._redis_conn) + await self._rpc_helper.init() self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) - await self._anchor_rpc_helper.init(redis_conn=self._redis_conn) + await self._anchor_rpc_helper.init() async def _init_rabbitmq_connection(self): """ diff --git a/snapshotter/snapshotter_id_ping.py b/snapshotter/snapshotter_id_ping.py index cb37b9f..a597eee 100644 --- a/snapshotter/snapshotter_id_ping.py +++ b/snapshotter/snapshotter_id_ping.py @@ -23,7 +23,7 @@ async def main(): # Initialize RPC helper for anchor chain anchor_rpc = RpcHelper(settings.anchor_chain_rpc) - await anchor_rpc.init(redis_conn=redis_conn) + await anchor_rpc.init() # Load protocol state ABI protocol_abi = read_json_file(settings.protocol_state.abi) diff --git a/snapshotter/system_event_detector.py b/snapshotter/system_event_detector.py index 7867295..47596b7 100644 --- a/snapshotter/system_event_detector.py +++ b/snapshotter/system_event_detector.py @@ -304,7 +304,7 @@ async def _detect_events(self): """ while True: try: - current_block = await self._anchor_rpc_helper.get_current_block(redis_conn=self._redis_conn) + current_block = await self._anchor_rpc_helper.get_current_block() self._logger.info('Current block: {}', current_block) except Exception as e: @@ -408,8 +408,8 @@ async def _init_rpc(self): """ Initializes the RpcHelper instances for both anchor and source chains. """ - await self._anchor_rpc_helper.init(redis_conn=self._redis_conn) - await self._source_rpc_helper.init(redis_conn=self._redis_conn) + await self._anchor_rpc_helper.init() + await self._source_rpc_helper.init() @rabbitmq_and_redis_cleanup def run(self): diff --git a/snapshotter/tests/test_web3_async_provider.py b/snapshotter/tests/test_web3_async_provider.py index f5c7088..b13068a 100644 --- a/snapshotter/tests/test_web3_async_provider.py +++ b/snapshotter/tests/test_web3_async_provider.py @@ -35,7 +35,7 @@ async def test_web3_async_call(): # Set up the RPC helper with the anchor chain RPC rpc_helper = RpcHelper(settings.anchor_chain_rpc) - await rpc_helper.init(writer_redis_pool) + await rpc_helper.init() # Create a synchronous Web3 client sync_w3_client = Web3(HTTPProvider(settings.anchor_chain_rpc.full_nodes[0].url)) @@ -52,7 +52,7 @@ async def test_web3_async_call(): ] # Execute the Web3 call asynchronously - result = await rpc_helper.web3_call(tasks, redis_conn=writer_redis_pool) + result = await rpc_helper.web3_call(tasks) logger.debug('Retrieve: {}', result) diff --git a/snapshotter/utils/generic_worker.py b/snapshotter/utils/generic_worker.py index ce219a8..337e93e 100644 --- a/snapshotter/utils/generic_worker.py +++ b/snapshotter/utils/generic_worker.py @@ -493,9 +493,9 @@ async def _init_rpc_helper(self): Initializes the RpcHelper objects for the worker and anchor chain, and sets up the protocol state contract. """ self._rpc_helper = RpcHelper(rpc_settings=settings.rpc) - await self._rpc_helper.init(redis_conn=self._redis_conn) + await self._rpc_helper.init() self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) - await self._anchor_rpc_helper.init(redis_conn=self._redis_conn) + await self._anchor_rpc_helper.init() await self._anchor_rpc_helper._load_async_web3_providers() self._protocol_state_contract = self._anchor_rpc_helper.get_current_node()['web3_client'].eth.contract( address=Web3.to_checksum_address( diff --git a/snapshotter/utils/preloaders/tx_receipts/delegated_worker/tx_receipts.py b/snapshotter/utils/preloaders/tx_receipts/delegated_worker/tx_receipts.py index a23e0d5..410375f 100644 --- a/snapshotter/utils/preloaders/tx_receipts/delegated_worker/tx_receipts.py +++ b/snapshotter/utils/preloaders/tx_receipts/delegated_worker/tx_receipts.py @@ -13,7 +13,7 @@ class TxReceiptProcessor(GenericDelegateProcessor): """ A processor class for handling transaction receipts. - + This class extends GenericDelegateProcessor and provides functionality to process and fetch transaction receipts. """ @@ -63,7 +63,6 @@ async def compute( # Fetch the transaction receipt tx_receipt_obj: web3.datastructures.AttributeDict = await rpc_helper.get_transaction_receipt( tx_hash, - redis_conn, ) # Convert AttributeDict to regular dictionary diff --git a/snapshotter/utils/snapshot_utils.py b/snapshotter/utils/snapshot_utils.py index 3f776b7..cb46f9d 100644 --- a/snapshotter/utils/snapshot_utils.py +++ b/snapshotter/utils/snapshot_utils.py @@ -52,7 +52,7 @@ async def get_block_details_in_block_range( return cached_details # Fetch block details from RPC if not cached - rpc_batch_block_details = await rpc_helper.batch_eth_get_block(from_block, to_block, redis_conn) + rpc_batch_block_details = await rpc_helper.batch_eth_get_block(from_block, to_block) rpc_batch_block_details = rpc_batch_block_details if rpc_batch_block_details else [] From cef275adc18aeeea25fcc788c83864a4bb81150f Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 16:20:40 -0400 Subject: [PATCH 08/19] chore: update poetry config for aiolimiter --- poetry.lock | 15 +++++++++++++-- pyproject.toml | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index e6ca471..a2837e7 100755 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aio-pika" @@ -138,6 +138,17 @@ yarl = ">=1.12.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +[[package]] +name = "aiolimiter" +version = "1.1.0" +description = "asyncio rate limiter, a leaky bucket implementation" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiolimiter-1.1.0-py3-none-any.whl", hash = "sha256:0b4997961fc58b8df40279e739f9cf0d3e255e63e9a44f64df567a8c17241e24"}, + {file = "aiolimiter-1.1.0.tar.gz", hash = "sha256:461cf02f82a29347340d031626c92853645c099cb5ff85577b831a7bd21132b5"}, +] + [[package]] name = "aiormq" version = "6.8.1" @@ -3117,4 +3128,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "e3576170e80f60997bbdac4a989546128f00f17410750f1157a45cd23282c78f" +content-hash = "0ee8cfeddf22b2b71b555f067e8d0052c0eb777cdf5b138760204bf3f9481267" diff --git a/pyproject.toml b/pyproject.toml index a69d499..462ab88 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ eip712-structs = "^1.1.0" coincurve = "^19.0.1" grpclib = {extras = ["protobuf"], version = "^0.4.7"} grpcio-tools = "^1.62.1" +aiolimiter = "^1.1.0" [build-system] From d79ef6c4153c0598ce2c069454044f26d4c4f2ad Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 22:07:10 -0400 Subject: [PATCH 09/19] chore: add unit tests for rpc helper module --- snapshotter/tests/rpc_helper_test.py | 215 ++++++++++++++++++ .../tests/static/abi/UUPSUpgradeable.json | 67 ++++++ .../tests/static/bytecode/protocol_state.json | 3 + .../static/bytecode/uups_upgradeable.json | 3 + 4 files changed, 288 insertions(+) create mode 100644 snapshotter/tests/rpc_helper_test.py create mode 100644 snapshotter/tests/static/abi/UUPSUpgradeable.json create mode 100644 snapshotter/tests/static/bytecode/protocol_state.json create mode 100644 snapshotter/tests/static/bytecode/uups_upgradeable.json diff --git a/snapshotter/tests/rpc_helper_test.py b/snapshotter/tests/rpc_helper_test.py new file mode 100644 index 0000000..cfbfcaa --- /dev/null +++ b/snapshotter/tests/rpc_helper_test.py @@ -0,0 +1,215 @@ +import asyncio +import json + +import pytest +from pytest_asyncio import fixture as async_fixture +from web3 import AsyncHTTPProvider +from web3 import AsyncWeb3 +from web3.contract import AsyncContract + +from snapshotter.settings.config import settings +from snapshotter.utils.models.settings_model import RateLimitConfig +from snapshotter.utils.models.settings_model import RPCConfigFull +from snapshotter.utils.models.settings_model import RPCNodeConfig +from snapshotter.utils.rpc import get_contract_abi_dict +from snapshotter.utils.rpc import get_event_sig_and_abi +from snapshotter.utils.rpc import RpcHelper + +# Custom RPC config for testing with Hardhat + +RATE_LIMIT_OVERRIDE = RateLimitConfig( + requests_per_second=1, + requests_per_minute=60, + requests_per_day=86400, +) + +TEST_RPC_CONFIG = RPCConfigFull( + full_nodes=[RPCNodeConfig(url='http://127.0.0.1:8545')], + archive_nodes=[RPCNodeConfig(url='http://127.0.0.1:8545')], + connection_limits=settings.rpc.connection_limits, + rate_limit=settings.rpc.rate_limit, + semaphore_value=settings.rpc.semaphore_value, + retry=settings.rpc.retry, + force_archive_blocks=settings.rpc.force_archive_blocks, + request_time_out=settings.rpc.request_time_out, + skip_epoch_threshold_blocks=settings.rpc.skip_epoch_threshold_blocks, + polling_interval=settings.rpc.polling_interval, +) + +@async_fixture(scope='module') +async def rpc_helper(): + helper = RpcHelper(rpc_settings=TEST_RPC_CONFIG) + await helper.init() + yield helper + +@async_fixture(scope='module') +async def rpc_helper_override(): + override_config = TEST_RPC_CONFIG + override_config.rate_limit = RATE_LIMIT_OVERRIDE + override_helper = RpcHelper(rpc_settings=override_config) + await override_helper.init() + yield override_helper + +@async_fixture(scope='module') +async def web3(): + yield AsyncWeb3(AsyncHTTPProvider('http://127.0.0.1:8545')) + +@async_fixture(scope='module') +async def protocol_contract(web3: AsyncWeb3): + # Load Implementation ABI and Bytecode + with open('snapshotter/static/abis/ProtocolContract.json', 'r') as abi_file: + implementation_abi = json.load(abi_file) + + with open('snapshotter/tests/static/bytecode/protocol_state.json', 'r') as bytecode_file: + implementation_bytecode_json = json.load(bytecode_file) + implementation_bytecode = implementation_bytecode_json['bytecode'] + + # Deploy the Implementation Contract + implementation_contract = web3.eth.contract(abi=implementation_abi, bytecode=implementation_bytecode) + tx_hash_impl = await implementation_contract.constructor().transact() + tx_receipt_impl = await web3.eth.wait_for_transaction_receipt(tx_hash_impl) + implementation_address = tx_receipt_impl['contractAddress'] + + accounts = await web3.eth.accounts + + # Load UUPSUpgradeable ABI and Bytecode + with open('snapshotter/tests/static/abi/UUPSUpgradeable.json', 'r') as proxy_abi_file: + proxy_abi = json.load(proxy_abi_file) + + with open('snapshotter/tests/static/bytecode/uups_upgradeable.json', 'r') as proxy_bytecode_file: + proxy_bytecode_json = json.load(proxy_bytecode_file) + proxy_bytecode = proxy_bytecode_json['bytecode'] + + # Initialize Proxy Constructor Parameters + # The initializer must be encoded using the implementation's initializer function + initializer = implementation_contract.encodeABI(fn_name='initialize', args=[web3.to_checksum_address(accounts[0])]) + + # Deploy the Proxy Contract + proxy_contract = web3.eth.contract(abi=proxy_abi, bytecode=proxy_bytecode) + tx_hash_proxy = await proxy_contract.constructor( + implementation_address, + initializer, + ).transact({'from': accounts[0]}) + tx_receipt_proxy = await web3.eth.wait_for_transaction_receipt(tx_hash_proxy) + proxy_address = tx_receipt_proxy['contractAddress'] + + proxy_instance = web3.eth.contract(address=proxy_address, abi=implementation_abi) + + # verify initialization + owner = await proxy_instance.functions.owner().call() + assert owner == web3.to_checksum_address(accounts[0]), 'Initialization failed.' + + yield proxy_instance + +@pytest.mark.asyncio(loop_scope='module') +async def test_get_current_block_number(rpc_helper, web3): + result = await rpc_helper.get_current_block_number() + assert isinstance(result, int) + assert result == await web3.eth.block_number + +@pytest.mark.asyncio(loop_scope='module') +async def test_get_transaction_receipt(rpc_helper, web3: AsyncWeb3, protocol_contract: AsyncContract): + accounts = await web3.eth.accounts + tx_hash = await protocol_contract.functions.updateSnapshotterState(accounts[0]).transact({'from': accounts[0]}) + await web3.eth.wait_for_transaction_receipt(tx_hash) + + result: dict = await rpc_helper.get_transaction_receipt(tx_hash.hex()) + assert result['transactionHash'] == tx_hash + assert 'blockNumber' in result + assert 'gasUsed' in result + +@pytest.mark.asyncio(loop_scope='module') +async def test_web3_call(rpc_helper, protocol_contract): + result: dict = await rpc_helper.web3_call([('owner', [])], protocol_contract.address, protocol_contract.abi) + assert result[0] == await protocol_contract.functions.owner().call() + +@pytest.mark.asyncio(loop_scope='module') +async def test_batch_eth_get_balance_on_block_range(rpc_helper: RpcHelper, web3: AsyncWeb3): + accounts = await web3.eth.accounts + account = accounts[0] + start_block = await web3.eth.block_number + + for _ in range(3): + tx_hash = await web3.eth.send_transaction({ + 'from': account, + 'to': accounts[1], + 'value': web3.to_wei(1, 'ether'), + }) + await web3.eth.wait_for_transaction_receipt(tx_hash) + + end_block = await web3.eth.block_number + balances = await rpc_helper.batch_eth_get_balance_on_block_range(account, start_block, end_block) + + assert len(balances) == end_block - start_block + 1 + assert all(isinstance(balance, int) for balance in balances) + assert balances[0] > balances[-1] + +@pytest.mark.asyncio(loop_scope='module') +async def test_batch_eth_call_on_block_range(rpc_helper: RpcHelper, web3: AsyncWeb3, protocol_contract: AsyncContract): + accounts = await web3.eth.accounts + start_block = await web3.eth.block_number + + for i in range(1, 4): + tx_hash = await protocol_contract.functions.updateSnapshotterState( + accounts[i], + ).transact({'from': accounts[0]}) + await web3.eth.wait_for_transaction_receipt(tx_hash) + + end_block = await web3.eth.block_number + + abi_dict = get_contract_abi_dict(protocol_contract.abi) + + results: list[tuple[int]] = await rpc_helper.batch_eth_call_on_block_range(abi_dict, 'snapshotterState', protocol_contract.address, start_block, end_block) + + assert len(results) == end_block - start_block + 1 + assert web3.to_checksum_address(results[-1][0]) == web3.to_checksum_address(accounts[3]) + +@pytest.mark.asyncio(loop_scope='module') +async def test_get_events_logs(rpc_helper: RpcHelper, web3: AsyncWeb3, protocol_contract: AsyncContract): + accounts = await web3.eth.accounts + start_block = await web3.eth.block_number + + for i in range(1, 4): + tx_hash = await protocol_contract.functions.transferOwnership( + accounts[i], + ).transact({'from': accounts[i-1]}) + await web3.eth.wait_for_transaction_receipt(tx_hash) + + end_block = await web3.eth.block_number + + EVENT_SIGS = { + 'OwnershipTransferred': 'OwnershipTransferred(address,address)', + } + + EVENT_ABI = { + 'OwnershipTransferred': protocol_contract.events.OwnershipTransferred._get_event_abi(), + } + + event_sig, event_abi = get_event_sig_and_abi(EVENT_SIGS, EVENT_ABI) + + events: list[dict[str, dict]] = await rpc_helper.get_events_logs( + protocol_contract.address, + end_block, + start_block, + event_sig, + event_abi, + ) + + assert len(events) == 3 + for i, event in enumerate(events): + assert event['event'] == 'OwnershipTransferred' + assert event['args']['previousOwner'] == accounts[i] + assert event['args']['newOwner'] == accounts[i+1] + +@pytest.mark.asyncio(loop_scope='module') +async def test_rate_limiting(rpc_helper_override: RpcHelper): + samples = 10 + start_time = asyncio.get_event_loop().time() + tasks = [rpc_helper_override.get_current_block_number() for _ in range(samples)] + await asyncio.gather(*tasks) + end_time = asyncio.get_event_loop().time() + + elapsed_time = end_time - start_time + expected_time = (samples - 1) / RATE_LIMIT_OVERRIDE.requests_per_second + + assert elapsed_time >= expected_time, f'Rate limiting not working as expected. Elapsed time: {elapsed_time}, Expected time: {expected_time}' diff --git a/snapshotter/tests/static/abi/UUPSUpgradeable.json b/snapshotter/tests/static/abi/UUPSUpgradeable.json new file mode 100644 index 0000000..67107cb --- /dev/null +++ b/snapshotter/tests/static/abi/UUPSUpgradeable.json @@ -0,0 +1,67 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + } +] diff --git a/snapshotter/tests/static/bytecode/protocol_state.json b/snapshotter/tests/static/bytecode/protocol_state.json new file mode 100644 index 0000000..3137192 --- /dev/null +++ b/snapshotter/tests/static/bytecode/protocol_state.json @@ -0,0 +1,3 @@ +{ +"bytecode": "0x60a0604052306080523480156200001557600080fd5b506200002062000026565b620000da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000775760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b60805161596c62000104600039600081816140040152818161402d0152614173015261596c6000f3fe6080604052600436106105185760003560e01c80637195df15116102a2578063b3d95efa11610165578063d72e07fd116100cc578063e72eeb9711610085578063e72eeb9714611204578063eb4c8b8914611231578063ef829a3d14611251578063f2fde38b1461126b578063f3354db01461128b578063ff8744a6146112ab57600080fd5b8063d72e07fd14611142578063da648f9214611162578063ded2465b1461118f578063e1d5fbce146111af578063e4578d51146111cf578063e59a4105146111ef57600080fd5b8063c2acc6a31161011e578063c2acc6a314611046578063c367e24414611066578063c4d66de8146110c2578063c9742dc1146110e2578063c9ab0c8314611102578063d4e1a3d11461112257600080fd5b8063b3d95efa14610f74578063b48753eb14610f94578063c00d0f9c14610fb4578063c12c2aa914610fd4578063c17b343414611006578063c19e74d91461102657600080fd5b80638da5cb5b11610209578063a02c3e9b116101c2578063a02c3e9b14610ea3578063a1ac8af014610ec3578063ad3cb1cc14610ee3578063b09c5e2f14610f14578063b1a3f28d14610f34578063b34aebca14610f5457600080fd5b80638da5cb5b14610dc457806392ae6f6614610e01578063948a463e14610e1657806397b0b79f14610e365780639a2458a614610e565780639ab1013d14610e7657600080fd5b80637f59285a1161025b5780637f59285a14610d045780637f9ee95014610d2457806383450d2614610d44578063865fb4eb14610d6457806389afe86a14610d845780638d3bcb5a14610da457600080fd5b80637195df1514610c2357806375fd5c7c14610c4357806379145b8914610c8457806379d0117514610ca45780637d2bd53d14610cc45780637e9ce89214610ce457600080fd5b806332f6f519116103ea5780635678a9d5116103515780636b0ad9ac1161030a5780636b0ad9ac14610b615780636ce4f26c14610b8e5780636e81f23414610bae5780636ee55d7314610bce578063715018a614610bee5780637174664414610c0357600080fd5b80635678a9d5146109fc57806359c60b3014610a1c5780635a5c908b14610a3c5780635edba3d314610a5c5780635f9abf7014610a7c578063665ebe8c14610b4157600080fd5b80634d9c25d4116103a35780634d9c25d4146109475780634f1ef2861461096757806350304b621461097a57806352d1902d1461099a57806352ec368a146109af57806353e28269146109cf57600080fd5b806332f6f51914610887578063342050cc146108a75780633cb782cd146108c75780633d15d0f4146108e757806340e29707146109075780634242614c1461092757600080fd5b80630a1b72271161048e57806320cfff831161044757806320cfff83146107b75780632564a9a6146107e757806327856ff3146108075780633163225514610827578063320eeed2146108475780633230df831461086757600080fd5b80630a1b7227146106db5780630d5a7a52146106fb5780630f34e6a91461071b5780631c7d13a61461073b5780631dbc586b146107685780631f5885881461078857600080fd5b806306b8d494116104e057806306b8d494146105d25780630736e19f146105f25780630857b13f1461062d57806309517cb61461067b578063095cb2101461069b5780630984dbd2146106bb57600080fd5b8063021434051461051d57806304a0a5bb1461055057806305237c1b146105705780630589852d14610592578063066522b0146105b2575b600080fd5b34801561052957600080fd5b5061053d610538366004614486565b6112cb565b6040519081526020015b60405180910390f35b34801561055c57600080fd5b5061053d61056b366004614486565b611335565b34801561057c57600080fd5b5061059061058b3660046144a3565b611375565b005b34801561059e57600080fd5b506105906105ad366004614523565b611493565b3480156105be57600080fd5b506105906105cd3660046145a5565b6115ba565b3480156105de57600080fd5b5061053d6105ed366004614486565b61162f565b3480156105fe57600080fd5b5061061261060d366004614486565b61166f565b60408051938452602084019290925290820152606001610547565b34801561063957600080fd5b5061066361064836600461461f565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610547565b34801561068757600080fd5b506105906106963660046144a3565b6116e3565b3480156106a757600080fd5b5061053d6106b6366004614486565b6118c2565b3480156106c757600080fd5b5061053d6106d6366004614486565b611902565b3480156106e757600080fd5b5061053d6106f6366004614486565b611942565b34801561070757600080fd5b50610663610716366004614486565b611982565b34801561072757600080fd5b5061053d610736366004614486565b6119e6565b34801561074757600080fd5b5061075b610756366004614486565b611a26565b604051610547919061468c565b34801561077457600080fd5b506106636107833660046146ad565b611a8e565b34801561079457600080fd5b506107a86107a33660046147d7565b611cd8565b60405161054793929190614863565b3480156107c357600080fd5b506107d76107d23660046144a3565b611d60565b6040519015158152602001610547565b3480156107f357600080fd5b5061053d610802366004614895565b611dde565b34801561081357600080fd5b506105906108223660046144a3565b611e4b565b34801561083357600080fd5b506105906108423660046148c1565b611f5e565b34801561085357600080fd5b5061053d610862366004614895565b61207c565b34801561087357600080fd5b5061053d610882366004614895565b6120ac565b34801561089357600080fd5b506105906108a2366004614486565b6120dc565b3480156108b357600080fd5b50600054610663906001600160a01b031681565b3480156108d357600080fd5b506105906108e2366004614895565b612199565b3480156108f357600080fd5b506107d7610902366004614486565b6121f8565b34801561091357600080fd5b5061053d610922366004614486565b612267565b34801561093357600080fd5b5061053d6109423660046148fc565b6122a7565b34801561095357600080fd5b5061053d610962366004614486565b6122d6565b61059061097536600461494b565b612316565b34801561098657600080fd5b50610590610995366004614523565b612335565b3480156109a657600080fd5b5061053d6123a0565b3480156109bb57600080fd5b506106636109ca3660046144a3565b6123bd565b3480156109db57600080fd5b5061053d6109ea3660046149a4565b60026020526000908152604090205481565b348015610a0857600080fd5b50610590610a17366004614a16565b612430565b348015610a2857600080fd5b50610590610a37366004614895565b6126f2565b348015610a4857600080fd5b50600154610663906001600160a01b031681565b348015610a6857600080fd5b5061053d610a77366004614486565b61271f565b348015610a8857600080fd5b50610aef610a97366004614486565b600560205260009081526040902080546001820154600283015460038401546004909401546001600160a01b0380851695600160a01b90950460ff908116958181169361010082049092169262010000909104169088565b604080516001600160a01b03998a16815260ff909816602089015287019590955260608601939093529015156080850152151560a08401529290921660c082015260e081019190915261010001610547565b348015610b4d57600080fd5b5061053d610b5c366004614486565b61275f565b348015610b6d57600080fd5b50610b81610b7c366004614486565b61279f565b6040516105479190614aec565b348015610b9a57600080fd5b50610663610ba9366004614486565b612807565b348015610bba57600080fd5b50610590610bc9366004614b39565b612847565b348015610bda57600080fd5b50610590610be9366004614895565b612875565b348015610bfa57600080fd5b50610590612917565b348015610c0f57600080fd5b50610590610c1e366004614486565b61292b565b348015610c2f57600080fd5b50610590610c3e366004614895565b612966565b348015610c4f57600080fd5b506107d7610c5e366004614486565b6001600160a01b0316600090815260056020526040902060030154610100900460ff1690565b348015610c9057600080fd5b50610590610c9f366004614895565b612993565b348015610cb057600080fd5b50610590610cbf3660046148fc565b6129c0565b348015610cd057600080fd5b50610590610cdf366004614bff565b6129ec565b348015610cf057600080fd5b5061075b610cff3660046147d7565b612c27565b348015610d1057600080fd5b5061053d610d1f366004614486565b612c9d565b348015610d3057600080fd5b5061053d610d3f3660046144a3565b612cdd565b348015610d5057600080fd5b506107d7610d5f366004614486565b612d51565b348015610d7057600080fd5b506107d7610d7f366004614486565b612d91565b348015610d9057600080fd5b50610590610d9f366004614895565b612dd1565b348015610db057600080fd5b50610590610dbf366004614c89565b612dfe565b348015610dd057600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610663565b348015610e0d57600080fd5b5061053d613051565b348015610e2257600080fd5b50610663610e313660046149a4565b6130ce565b348015610e4257600080fd5b5061053d610e513660046144a3565b613118565b348015610e6257600080fd5b5061053d610e71366004614895565b61314f565b348015610e8257600080fd5b5061053d610e913660046149a4565b60009081526002602052604090205490565b348015610eaf57600080fd5b50610590610ebe366004614895565b61317f565b348015610ecf57600080fd5b50610590610ede3660046148c1565b6131ac565b348015610eef57600080fd5b5061075b604051806040016040528060058152602001640352e302e360dc1b81525081565b348015610f2057600080fd5b506107d7610f2f366004614d1d565b6131e7565b348015610f4057600080fd5b50610590610f4f366004614d5f565b613220565b348015610f6057600080fd5b50610590610f6f366004614dc6565b613333565b348015610f8057600080fd5b5061053d610f8f366004614486565b61336f565b348015610fa057600080fd5b50610590610faf366004614486565b6133af565b348015610fc057600080fd5b506107d7610fcf3660046144a3565b6133d9565b348015610fe057600080fd5b50610ff4610fef366004614486565b613410565b60405160ff9091168152602001610547565b34801561101257600080fd5b506107d76110213660046148fc565b613474565b34801561103257600080fd5b50610590611041366004614e73565b6134e4565b34801561105257600080fd5b5061053d611061366004614486565b613607565b34801561107257600080fd5b50611086611081366004614895565b613647565b6040516105479190815181526020808301516001600160a01b031690820152604080830151908201526060918201519181019190915260800190565b3480156110ce57600080fd5b506105906110dd366004614486565b6136e4565b3480156110ee57600080fd5b506105906110fd366004614486565b6137f9565b34801561110e57600080fd5b5061061261111d366004614895565b613823565b34801561112e57600080fd5b506107d761113d366004614895565b6138a4565b34801561114e57600080fd5b5061059061115d366004614895565b6138d4565b34801561116e57600080fd5b5061118261117d366004614895565b613901565b6040516105479190614f84565b34801561119b57600080fd5b5061053d6111aa3660046148fc565b613971565b3480156111bb57600080fd5b5061053d6111ca366004614486565b6139a0565b3480156111db57600080fd5b506105906111ea366004614895565b6139e0565b3480156111fb57600080fd5b5061053d613a0d565b34801561121057600080fd5b5061122461121f366004614895565b613a61565b6040516105479190614fd3565b34801561123d57600080fd5b5061059061124c366004614895565b613ad1565b34801561125d57600080fd5b50600354610ff49060ff1681565b34801561127757600080fd5b50610590611286366004614486565b613afe565b34801561129757600080fd5b5061053d6112a6366004614486565b613b3c565b3480156112b757600080fd5b50610b816112c6366004614486565b613b7c565b6000816001600160a01b03166399332c5e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f9190614fe6565b92915050565b6000816001600160a01b031663ae423daf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6040516321a9b3f960e01b815260048101839052602481018290526000906001600160a01b038516906321a9b3f9906044016020604051808303816000875af11580156113c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ea9190614fff565b9050801561143c578282856001600160a01b03167f4a87247b65ffdb6c5ebb776b6e70fc9bddc3402b413d82060701fd9c30a3ff074260405161142f91815260200190565b60405180910390a461148d565b611447848484613bbc565b8282856001600160a01b03167f9737b8e7fb3913ba98706f4b1758ac14f5cf26afbb2457117aa3360b9cc85de14260405161148491815260200190565b60405180910390a45b50505050565b6040516360a1c32160e11b81526001600160a01b0386169063c1438642906114c5908790879087908790600401615115565b600060405180830381600087803b1580156114df57600080fd5b505af11580156114f3573d6000803e3d6000fd5b505050506000856001600160a01b031663766718086040518163ffffffff1660e01b8152600401606060405180830381865afa158015611537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155b9190615147565b925050506001600160a01b0386167fcbf1b93d76451f05244e2f6139bf7266a14bac5182e5ed8981ab0ce36479efbf8686868661159987600161518b565b6040516115aa95949392919061519e565b60405180910390a2505050505050565b6115c2613f9e565b6000546040516266522b60e41b81526001600160a01b039091169063066522b0906115f7908790879087908790600401615216565b600060405180830381600087803b15801561161157600080fd5b505af1158015611625573d6000803e3d6000fd5b5050505050505050565b6000816001600160a01b031663cf0042176040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6000806000836001600160a01b031663766718086040518163ffffffff1660e01b8152600401606060405180830381865afa1580156116b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d69190615147565b9250925092509193909250565b60405163132c290f60e01b8152600481018390526024810182905260009081906001600160a01b0386169063132c290f9060440160408051808303816000875af1158015611735573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611759919061525d565b91509150811561180757846001600160a01b03167f9bf380fe36617cd5d995968abb4ae6d3657a763c126535d590b4503ff2542426866001600160a01b03166399332c5e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f09190614fe6565b604080519182524260208301520160405180910390a25b80156118bb57846001600160a01b03166323e780776040518163ffffffff1660e01b8152600401602060405180830381865afa15801561184b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186f9190614fe6565b6040805186815260208101869052428183015290516001600160a01b038816917ff7d2257d4a1c445138ab52bd3c22425bfed29da81d0173961c697dc14fcba60c919081900360600190a35b5050505050565b6000816001600160a01b0316630220499f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6000816001600160a01b03166366752e1e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6000816001600160a01b031663e3042b176040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6000816001600160a01b031663e2d2bfe36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f919061528c565b6000816001600160a01b031663351b61556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6060816001600160a01b03166304a78fca6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611a66573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261132f91908101906152f6565b6000611a98613f9e565b6001546040516312dae64360e21b81526001600160a01b03888116600483015260ff88166024830152604482018790526064820186905284151560848301523060a48301526000921690634b6b990c9060c4016020604051808303816000875af1158015611b0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2e919061528c565b6040805161010080820183526001600160a01b03808c16835260ff808c1660208086019182528587018d8152606087018d81528c151560808901908152600160a08a01818152888d1660c08c018181524260e08e01908152600092835260059098529c81209b518c5498518a16600160a01b026001600160a81b0319909916908b1617979097178b559351908a015590516002890155516003808901805493519a51909716620100000262010000600160b01b03199a151590980261ff00199215159290921661ffff1990931692909217179790971694909417909255915160049093019290925582549394509290921691611c298361532a565b82546101009290920a60ff818102199093169183160217909155600354811660009081526004602090815260409182902080546001600160a01b0319166001600160a01b038781169182179092558351948c1685529184018a9052918301889052861515606084015230608084015260a0830152891691507f4ac24bfdd2a01328b05db8f3587611f1d37fd01718e1edaa23a75ec91d71f5179060c00160405180910390a29695505050505050565b600060606000856001600160a01b0316633aaf384d86866040518363ffffffff1660e01b8152600401611d0c929190615349565b600060405180830381865afa158015611d29573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d51919081019061536b565b92509250925093509350939050565b60405163ac23e9ff60e01b815260048101839052602481018290526000906001600160a01b0385169063ac23e9ff906044015b602060405180830381865afa158015611db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd49190614fff565b90505b9392505050565b604051639d032ff160e01b8152600481018290526000906001600160a01b03841690639d032ff1906024015b602060405180830381865afa158015611e27573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd79190614fe6565b604051637a9bd1f160e11b815260048101839052602481018290526001600160a01b0384169063f537a3e290604401600060405180830381600087803b158015611e9457600080fd5b505af1158015611ea8573d6000803e3d6000fd5b50505050826001600160a01b03166323e780776040518163ffffffff1660e01b8152600401602060405180830381865afa158015611eea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0e9190614fe6565b6040805184815260208101849052428183015290516001600160a01b038616917ff7d2257d4a1c445138ab52bd3c22425bfed29da81d0173961c697dc14fcba60c919081900360600190a3505050565b60405163386ff39b60e11b81526004810184905260248101839052604481018290526000906001600160a01b038616906370dfe736906064016020604051808303816000875af1158015611fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fda9190614fff565b9050801561202e5760408051858152426020820152339185916001600160a01b038916917ff4b2e45e85a2dfbff1f3d17d57722c58599f10a2a3b9764b6106e7ca0c21d22f910160405180910390a46118bb565b60408051858152426020820152339185916001600160a01b038916917f4fd04f28641379ddef7bacd546c5e698814831a1c0772236c460aa42b029aa31910160405180910390a45050505050565b60405163198a844560e31b8152600481018290526000906001600160a01b0384169063cc54222890602401611e0a565b604051631864765960e11b8152600481018290526000906001600160a01b038416906330c8ecb290602401611e0a565b806001600160a01b031663f6543f63826001600160a01b0316632e70a14d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214d9190614fff565b6040516001600160e01b031960e084901b16815290156004820152602401600060405180830381600087803b15801561218557600080fd5b505af11580156118bb573d6000803e3d6000fd5b6040516338deacd360e01b8152600481018290526001600160a01b038316906338deacd3906024015b600060405180830381600087803b1580156121dc57600080fd5b505af11580156121f0573d6000803e3d6000fd5b505050505050565b60008054604051630f45743d60e21b81526001600160a01b03848116600483015290911690633d15d0f490602401602060405180830381865afa158015612243573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f9190614fff565b6000816001600160a01b03166374be21506040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6040516307d186df60e51b81526000906001600160a01b0384169063fa30dbe090611e0a90859060040161468c565b6000816001600160a01b031663b398c2906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b61231e613ff9565b6123278261409e565b61233182826140a6565b5050565b60405163c540b5ab60e01b81526001600160a01b0386169063c540b5ab906123679087908790879087906004016153c4565b600060405180830381600087803b15801561238157600080fd5b505af1158015612395573d6000803e3d6000fd5b505050505050505050565b60006123aa614168565b5060008051602061591783398151915290565b60405163157e859d60e01b815260048101839052602481018290526000906001600160a01b0385169063157e859d90604401602060405180830381865afa15801561240c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd4919061528c565b876001600160a01b0316632e70a14d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561246e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124929190614fff565b6124e35760405162461bcd60e51b815260206004820152601f60248201527f446174614d61726b65742066616c6c6261636b206e6f7420656e61626c65640060448201526064015b60405180910390fd5b604051634d93cfdf60e11b81526000906001600160a01b038a169063c655d7aa908290639b279fbe9061251a908990600401615441565b602060405180830381865afa158015612537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255b9190614fe6565b85856040518463ffffffff1660e01b815260040161257b93929190615454565b602060405180830381865afa158015612598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125bc919061528c565b90506000808a6001600160a01b0316630fbe203a8b8b8b8b8b8b8b6040518863ffffffff1660e01b81526004016125f9979695949392919061546e565b60408051808303816000875af1158015612617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061263b91906154d6565b91509150811561269257878b6001600160a01b03167f1746fd35c2b9c905f8e7ef34232f796acb536f2fd87f01d201f57fb338eab9a6838a8d426040516126859493929190615504565b60405180910390a36126e5565b87836001600160a01b03168c6001600160a01b03167f3857c02d90218ce4e6decef6b24babba8e0d8331c159392de06c1ce2c7a2d3be8d8d8c426040516126dc9493929190615504565b60405180910390a45b5050505050505050505050565b6040516382cdfd4360e01b8152600481018290526001600160a01b038316906382cdfd43906024016121c2565b6000816001600160a01b0316630a763da16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6000816001600160a01b031663ab65a54c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6060816001600160a01b031663125c5f166040518163ffffffff1660e01b8152600401600060405180830381865afa1580156127df573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261132f9190810190615541565b6000816001600160a01b031663f3ac5e926040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c2573d6000803e3d6000fd5b604051630257da6960e11b81526001600160a01b0382811660048301528316906304afb4d2906024016121c2565b6040516334c029a960e01b8152600481018290526001600160a01b038316906334c029a990602401600060405180830381600087803b1580156128b757600080fd5b505af11580156128cb573d6000803e3d6000fd5b5050505080826001600160a01b03167f5ed90e6a0e12831302fecf13a2cc6f7e9439fed821340d0bddc4ae305dbf9c304260405161290b91815260200190565b60405180910390a35050565b61291f613f9e565b61292960006141b1565b565b806001600160a01b031663952684086040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561218557600080fd5b6040516321b2118960e21b8152600481018290526001600160a01b038316906386c84624906024016121c2565b604051634036d79760e11b8152600481018290526001600160a01b0383169063806daf2e906024016121c2565b60405163017aff0d60e21b81526001600160a01b038316906305ebfc34906121c290849060040161468c565b60005b8351811015612b2a576000838281518110612a0c57612a0c6155cf565b60200260200101519050856001600160a01b0316630220499f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a789190614fe6565b8110612b2157856001600160a01b0316637e9834d76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612abc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae09190614fe6565b60026000878581518110612af657612af66155cf565b602002602001015181526020019081526020016000206000828254612b1b919061518b565b90915550505b506001016129ef565b50604051630a9dab6160e11b81526000906001600160a01b0386169063153b56c290612b5e908790879087906004016155e5565b6020604051808303816000875af1158015612b7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba19190614fff565b905080156118bb57846001600160a01b03167f7da483c8dd175f1c370a23b17545e19c0584cf3960d991202e216e6cf95b7a3d3386600081518110612be857612be86155cf565b602090810291909101810151604080516001600160a01b0390941684529183015281018590524260608201526080015b60405180910390a25050505050565b6040516330ae5f5360e21b81526060906001600160a01b0385169063c2b97d4c90612c589086908690600401615349565b600060405180830381865afa158015612c75573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dd491908101906152f6565b6000816001600160a01b0316637e9834d76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b60405163276f141960e21b815260048101839052602481018290526000906001600160a01b03851690639dbc5064906044015b602060405180830381865afa158015612d2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd49190614fe6565b6000816001600160a01b0316631dafe16b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612243573d6000803e3d6000fd5b6000816001600160a01b0316632d46247b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612243573d6000803e3d6000fd5b6040516382265a8760e01b8152600481018290526001600160a01b038316906382265a87906024016121c2565b60405163a5240d2360e01b81526000906001600160a01b0388169063a5240d2390612e35908990899089908990899060040161561b565b6020604051808303816000875af1158015612e54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e78919061565d565b905060005b84811015611625576000826002811115612e9957612e9961482f565b03612f3e57876001600160a01b03167f2a93c48a2a98c035cd37b0e8a3e25c15ce5dd0caa1fb0553603c249c37db24b8878784818110612edb57612edb6155cf565b9050602002016020810190612ef09190614486565b868685818110612f0257612f026155cf565b9050602002016020810190612f17919061567a565b604080516001600160a01b03909316835290151560208301520160405180910390a2613049565b6001826002811115612f5257612f5261482f565b03612f9457876001600160a01b03167fad12010237fe83915c67abe51836e693a16f8a2592b9a4e959374ab33ae7a697878784818110612edb57612edb6155cf565b6002826002811115612fa857612fa861482f565b0361304957876001600160a01b03167fcde1efc8de533d8d3476a1e5f7423ea506a579776fc27e7d5f963f6c6018ca39878784818110612fea57612fea6155cf565b9050602002016020810190612fff9190614486565b868685818110613011576130116155cf565b9050602002016020810190613026919061567a565b604080516001600160a01b03909316835290151560208301520160405180910390a25b600101612e7d565b60008060009054906101000a90046001600160a01b03166001600160a01b03166392ae6f666040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130c99190614fe6565b905090565b60008054604051634a45231f60e11b8152600481018490526001600160a01b039091169063948a463e90602401602060405180830381865afa1580156119c2573d6000803e3d6000fd5b60405163337ca1a360e11b815260048101839052602481018290526000906001600160a01b038516906366f9434690604401612d10565b60405163486429e760e01b8152600481018290526000906001600160a01b0384169063486429e790602401611e0a565b604051634d97c4e760e11b8152600481018290526001600160a01b03831690639b2f89ce906024016121c2565b60405163bb8ab44b60e01b81526004810184905260248101839052604481018290526001600160a01b0385169063bb8ab44b906064016115f7565b604051630cd0828360e21b8152600481018390526001600160a01b038281166024830152600091908516906333420a0c90604401611d93565b60405163562209f360e01b81526001600160a01b0385169063562209f39061325090869086908690600401615697565b600060405180830381600087803b15801561326a57600080fd5b505af115801561327e573d6000803e3d6000fd5b505050506000846001600160a01b031663766718086040518163ffffffff1660e01b8152600401606060405180830381865afa1580156132c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132e69190615147565b925050506001600160a01b0385167f3c6dc99dfc227a11ad701f84af7d44db829ba6c5e71c85f0ba80da02a2c20b4285858561332386600161518b565b604051612c1894939291906156bd565b61333b613f9e565b6001600160a01b03909116600090815260056020526040902060030180549115156101000261ff0019909216919091179055565b6000816001600160a01b0316630f827df96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6133b7613f9e565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60405163d1dd6ddd60e01b815260048101839052602481018290526000906001600160a01b0385169063d1dd6ddd90604401611d93565b6000816001600160a01b031663626560036040518163ffffffff1660e01b8152600401602060405180830381865afa158015613450573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132f91906156e4565b6040516304d76d5960e01b81526000906001600160a01b038416906304d76d59906134a390859060040161468c565b602060405180830381865afa1580156134c0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd79190614fff565b600080886001600160a01b031663872f46748989898989896040518763ffffffff1660e01b815260040161351d96959493929190615701565b60408051808303816000875af115801561353b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061355f919061525d565b9150915081156135b05785896001600160a01b03167f0b4031b6dda76fc423ccf9fba3aa5b0936474be3c9b7080c165c0744a002fe75898b426040516135a79392919061486c565b60405180910390a35b80156123955785896001600160a01b03167ff2de85dca20817401360fd386051732f208a7508ed4ffa7c15686979da276ec6898b426040516135f49392919061486c565b60405180910390a3505050505050505050565b6000816001600160a01b031663983d52e76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b61367b60405180608001604052806000815260200160006001600160a01b0316815260200160008152602001600081525090565b604051632f883e6b60e21b8152600481018390526001600160a01b0384169063be20f9ac90602401608060405180830381865afa1580156136c0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd7919061575a565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03166000811580156137295750825b90506000826001600160401b031660011480156137455750303b155b905081158015613753575080155b156137715760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561379b57845460ff60401b1916600160401b1785555b6137a486614222565b6137ac614233565b83156121f057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050505050565b613801613f9e565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000846001600160a01b0316633894228e856040518263ffffffff1660e01b815260040161385691815260200190565b606060405180830381865afa158015613873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138979190615147565b9250925092509250925092565b60405163a047977d60e01b8152600481018290526000906001600160a01b0384169063a047977d906024016134a3565b604051630c7511d160e21b8152600481018290526001600160a01b038316906331d44744906024016121c2565b60405163b4bf20e360e01b8152600481018290526060906001600160a01b0384169063b4bf20e390602401600060405180830381865afa158015613949573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dd791908101906157c5565b604051632750b58560e11b81526000906001600160a01b03841690634ea16b0a90611e0a90859060040161468c565b6000816001600160a01b03166325129a3f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6040516311059ad360e31b8152600481018290526001600160a01b0383169063882cd698906024016121c2565b60008060009054906101000a90046001600160a01b03166001600160a01b031663e59a41056040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130a5573d6000803e3d6000fd5b6040516343ffa62160e11b8152600481018290526060906001600160a01b038416906387ff4c4290602401600060405180830381865afa158015613aa9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dd79190810190615875565b6040516306d26e6360e41b8152600481018290526001600160a01b03831690636d26e630906024016121c2565b613b06613f9e565b6001600160a01b038116613b3057604051631e4fbdf760e01b8152600060048201526024016124da565b613b39816141b1565b50565b6000816001600160a01b031663059080f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130b573d6000803e3d6000fd5b6060816001600160a01b031663b7ab4db56040518163ffffffff1660e01b8152600401600060405180830381865afa1580156127df573d6000803e3d6000fd5b60005b604051633461783f60e01b8152600481018490526001600160a01b03851690633461783f90602401602060405180830381865afa158015613c04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c289190614fe6565b811015613cef5760405163157e859d60e01b81526004810184905260248101829052839083906001600160a01b038716907f714caf86f735bcfc9ca5f4e56456c4c16a6630870eaee41fa5748b2502a1317a90829063157e859d90604401602060405180830381865afa158015613ca3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cc7919061528c565b604080516001600160a01b0390921682524260208301520160405180910390a4600101613bbf565b50604051631c4a114760e11b8152600481018290526000906001600160a01b03851690633894228e90602401606060405180830381865afa158015613d38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d5c9190615147565b9250505060005b60405163dd64ea7160e01b8152600481018590526001600160a01b0386169063dd64ea7190602401602060405180830381865afa158015613da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcc9190614fe6565b8110156118bb5760005b60405163dd64ea7160e01b8152600481018690526001600160a01b0387169063dd64ea7190602401602060405180830381865afa158015613e1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e3f9190614fe6565b811015613f955760405163144327d360e21b815260048101869052602481018290526000906001600160a01b0388169063510c9f4c90604401600060405180830381865afa158015613e95573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613ebd91908101906152f6565b90506000876001600160a01b0316633aaf384d83886040518363ffffffff1660e01b8152600401613eef929190615349565b600060405180830381865afa158015613f0c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613f34919081019061536b565b50915050600081511115613f8b5785886001600160a01b03167f1746fd35c2b9c905f8e7ef34232f796acb536f2fd87f01d201f57fb338eab9a687858542604051613f829493929190615504565b60405180910390a35b5050600101613dd6565b50600101613d63565b33613fd07f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146129295760405163118cdaa760e01b81523360048201526024016124da565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061408057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316614074600080516020615917833981519152546001600160a01b031690565b6001600160a01b031614155b156129295760405163703e46dd60e11b815260040160405180910390fd5b613b39613f9e565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015614100575060408051601f3d908101601f191682019092526140fd91810190614fe6565b60015b61412857604051634c9c8ce360e01b81526001600160a01b03831660048201526024016124da565b600080516020615917833981519152811461415957604051632a87526960e21b8152600481018290526024016124da565b614163838361423b565b505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146129295760405163703e46dd60e11b815260040160405180910390fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61422a614291565b613b39816142da565b612929614291565b614244826142e2565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115614289576141638282614347565b6123316143bd565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661292957604051631afcd79f60e31b815260040160405180910390fd5b613b06614291565b806001600160a01b03163b60000361431857604051634c9c8ce360e01b81526001600160a01b03821660048201526024016124da565b60008051602061591783398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b03168460405161436491906158fa565b600060405180830381855af49150503d806000811461439f576040519150601f19603f3d011682016040523d82523d6000602084013e6143a4565b606091505b50915091506143b48583836143dc565b95945050505050565b34156129295760405163b398979f60e01b815260040160405180910390fd5b6060826143f1576143ec82614438565b611dd7565b815115801561440857506001600160a01b0384163b155b1561443157604051639996b31560e01b81526001600160a01b03851660048201526024016124da565b5080611dd7565b8051156144485780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114613b3957600080fd5b803561448181614461565b919050565b60006020828403121561449857600080fd5b8135611dd781614461565b6000806000606084860312156144b857600080fd5b83356144c381614461565b95602085013595506040909401359392505050565b60008083601f8401126144ea57600080fd5b5081356001600160401b0381111561450157600080fd5b6020830191508360208260051b850101111561451c57600080fd5b9250929050565b60008060008060006060868803121561453b57600080fd5b853561454681614461565b945060208601356001600160401b038082111561456257600080fd5b61456e89838a016144d8565b9096509450604088013591508082111561458757600080fd5b50614594888289016144d8565b969995985093965092949392505050565b600080600080604085870312156145bb57600080fd5b84356001600160401b03808211156145d257600080fd5b6145de888389016144d8565b909650945060208701359150808211156145f757600080fd5b50614604878288016144d8565b95989497509550505050565b60ff81168114613b3957600080fd5b60006020828403121561463157600080fd5b8135611dd781614610565b60005b8381101561465757818101518382015260200161463f565b50506000910152565b6000815180845261467881602086016020860161463c565b601f01601f19169290920160200192915050565b602081526000611dd76020830184614660565b8015158114613b3957600080fd5b600080600080600060a086880312156146c557600080fd5b85356146d081614461565b945060208601356146e081614610565b9350604086013592506060860135915060808601356146fe8161469f565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561474a5761474a61470c565b604052919050565b60006001600160401b0382111561476b5761476b61470c565b50601f01601f191660200190565b600061478c61478784614752565b614722565b90508281528383830111156147a057600080fd5b828260208301376000602084830101529392505050565b600082601f8301126147c857600080fd5b611dd783833560208501614779565b6000806000606084860312156147ec57600080fd5b83356147f781614461565b925060208401356001600160401b0381111561481257600080fd5b61481e868287016147b7565b925050604084013590509250925092565b634e487b7160e01b600052602160045260246000fd5b60038110613b3957634e487b7160e01b600052602160045260246000fd5b61486c84614845565b8381526060602082015260006148856060830185614660565b9050826040830152949350505050565b600080604083850312156148a857600080fd5b82356148b381614461565b946020939093013593505050565b600080600080608085870312156148d757600080fd5b84356148e281614461565b966020860135965060408601359560600135945092505050565b6000806040838503121561490f57600080fd5b823561491a81614461565b915060208301356001600160401b0381111561493557600080fd5b614941858286016147b7565b9150509250929050565b6000806040838503121561495e57600080fd5b823561496981614461565b915060208301356001600160401b0381111561498457600080fd5b8301601f8101851361499557600080fd5b61494185823560208401614779565b6000602082840312156149b657600080fd5b5035919050565b600060a082840312156149cf57600080fd5b50919050565b60008083601f8401126149e757600080fd5b5081356001600160401b038111156149fe57600080fd5b60208301915083602082850101111561451c57600080fd5b60008060008060008060008060e0898b031215614a3257600080fd5b614a3b89614476565b97506020890135965060408901356001600160401b0380821115614a5e57600080fd5b614a6a8c838d016147b7565b975060608b0135965060808b0135915080821115614a8757600080fd5b614a938c838d016147b7565b955060a08b0135915080821115614aa957600080fd5b614ab58c838d016149bd565b945060c08b0135915080821115614acb57600080fd5b50614ad88b828c016149d5565b999c989b5096995094979396929594505050565b6020808252825182820181905260009190848201906040850190845b81811015614b2d5783516001600160a01b031683529284019291840191600101614b08565b50909695505050505050565b60008060408385031215614b4c57600080fd5b8235614b5781614461565b91506020830135614b6781614461565b809150509250929050565b60006001600160401b03821115614b8b57614b8b61470c565b5060051b60200190565b600082601f830112614ba657600080fd5b81356020614bb661478783614b72565b8083825260208201915060208460051b870101935086841115614bd857600080fd5b602086015b84811015614bf45780358352918301918301614bdd565b509695505050505050565b60008060008060808587031215614c1557600080fd5b8435614c2081614461565b935060208501356001600160401b0380821115614c3c57600080fd5b614c4888838901614b95565b94506040870135915080821115614c5e57600080fd5b50614c6b87828801614b95565b949793965093946060013593505050565b60038110613b3957600080fd5b60008060008060008060808789031215614ca257600080fd5b8635614cad81614461565b95506020870135614cbd81614c7c565b945060408701356001600160401b0380821115614cd957600080fd5b614ce58a838b016144d8565b90965094506060890135915080821115614cfe57600080fd5b50614d0b89828a016144d8565b979a9699509497509295939492505050565b600080600060608486031215614d3257600080fd5b8335614d3d81614461565b9250602084013591506040840135614d5481614461565b809150509250925092565b60008060008060608587031215614d7557600080fd5b8435614d8081614461565b935060208501356001600160401b03811115614d9b57600080fd5b614da7878288016149d5565b9094509250506040850135614dbb8161469f565b939692955090935050565b60008060408385031215614dd957600080fd5b8235614de481614461565b91506020830135614b678161469f565b600082601f830112614e0557600080fd5b81356020614e1561478783614b72565b82815260059290921b84018101918181019086841115614e3457600080fd5b8286015b84811015614bf45780356001600160401b03811115614e575760008081fd5b614e658986838b01016147b7565b845250918301918301614e38565b600080600080600080600060e0888a031215614e8e57600080fd5b8735614e9981614461565b965060208801356001600160401b0380821115614eb557600080fd5b614ec18b838c016147b7565b975060408a0135965060608a0135955060808a0135915080821115614ee557600080fd5b614ef18b838c01614df4565b945060a08a0135915080821115614f0757600080fd5b50614f148a828b01614df4565b92505060c0880135905092959891949750929550565b60008282518085526020808601955060208260051b8401016020860160005b84811015614f7757601f19868403018952614f65838351614660565b98840198925090830190600101614f49565b5090979650505050505050565b602081526000611dd76020830184614f2a565b60008151808452602080850194506020840160005b83811015614fc857815187529582019590820190600101614fac565b509495945050505050565b602081526000611dd76020830184614f97565b600060208284031215614ff857600080fd5b5051919050565b60006020828403121561501157600080fd5b8151611dd78161469f565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e1984360301811261505c57600080fd5b83016020810192503590506001600160401b0381111561507b57600080fd5b80360382131561451c57600080fd5b6000838385526020808601955060208560051b8301018460005b87811015614f7757848303601f190189526150bf8288615045565b6150ca85828461501c565b9a86019a94505050908301906001016150a4565b8183526000602080850194508260005b85811015614fc85781356151018161469f565b1515875295820195908201906001016150ee565b60408152600061512960408301868861508a565b828103602084015261513c8185876150de565b979650505050505050565b60008060006060848603121561515c57600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052601160045260246000fd5b8082018082111561132f5761132f615175565b6060815260006151b260608301878961508a565b82810360208401526151c58186886150de565b9150508260408301529695505050505050565b8183526000602080850194508260005b85811015614fc85781356151fb81614461565b6001600160a01b0316875295820195908201906001016151e8565b6040808252810184905260006001600160fb1b0385111561523657600080fd5b8460051b8087606085013782018281036060908101602085015261513c90820185876151d8565b6000806040838503121561527057600080fd5b825161527b8161469f565b6020840151909250614b678161469f565b60006020828403121561529e57600080fd5b8151611dd781614461565b600082601f8301126152ba57600080fd5b81516152c861478782614752565b8181528460208386010111156152dd57600080fd5b6152ee82602083016020870161463c565b949350505050565b60006020828403121561530857600080fd5b81516001600160401b0381111561531e57600080fd5b6152ee848285016152a9565b600060ff821660ff810361534057615340615175565b60010192915050565b60408152600061535c6040830185614660565b90508260208301529392505050565b60008060006060848603121561538057600080fd5b835161538b81614c7c565b60208501519093506001600160401b038111156153a757600080fd5b6153b3868287016152a9565b925050604084015190509250925092565b6040815260006151296040830186886151d8565b803582526020810135602083015260006153f56040830183615045565b60a0604086015261540a60a08601828461501c565b915050606083013560608501526154246080840184615045565b858303608087015261543783828461501c565b9695505050505050565b602081526000611dd760208301846153d8565b8381526040602082015260006143b460408301848661501c565b87815260c06020820152600061548760c0830189614660565b876040840152828103606084015261549f8188614660565b905082810360808401526154b381876153d8565b905082810360a08401526154c881858761501c565b9a9950505050505050505050565b600080604083850312156154e957600080fd5b82516154f48161469f565b6020939093015192949293505050565b84815260806020820152600061551d6080830186614660565b828103604084015261552f8186614660565b91505082606083015295945050505050565b6000602080838503121561555457600080fd5b82516001600160401b0381111561556a57600080fd5b8301601f8101851361557b57600080fd5b805161558961478782614b72565b81815260059190911b820183019083810190878311156155a857600080fd5b928401925b8284101561513c5783516155c081614461565b825292840192908401906155ad565b634e487b7160e01b600052603260045260246000fd5b6060815260006155f86060830186614f97565b828103602084015261560a8186614f97565b915050826040830152949350505050565b61562486614845565b85815260606020820152600061563e6060830186886151d8565b82810360408401526156518185876150de565b98975050505050505050565b60006020828403121561566f57600080fd5b8151611dd781614c7c565b60006020828403121561568c57600080fd5b8135611dd78161469f565b6040815260006156ab60408301858761501c565b90508215156020830152949350505050565b6060815260006156d160608301868861501c565b9315156020830152506040015292915050565b6000602082840312156156f657600080fd5b8151611dd781614610565b60c08152600061571460c0830189614660565b87602084015286604084015282810360608401526157328187614f2a565b905082810360808401526157468186614f2a565b9150508260a0830152979650505050505050565b60006080828403121561576c57600080fd5b604051608081018181106001600160401b038211171561578e5761578e61470c565b6040528251815260208301516157a381614461565b6020820152604083810151908201526060928301519281019290925250919050565b600060208083850312156157d857600080fd5b82516001600160401b03808211156157ef57600080fd5b818501915085601f83011261580357600080fd5b815161581161478782614b72565b81815260059190911b8301840190848101908883111561583057600080fd5b8585015b838110156158685780518581111561584c5760008081fd5b61585a8b89838a01016152a9565b845250918601918601615834565b5098975050505050505050565b6000602080838503121561588857600080fd5b82516001600160401b0381111561589e57600080fd5b8301601f810185136158af57600080fd5b80516158bd61478782614b72565b81815260059190911b820183019083810190878311156158dc57600080fd5b928401925b8284101561513c578351825292840192908401906158e1565b6000825161590c81846020870161463c565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220aa1c1ab8a8fb9f9a43c2b0588d6d00b2f86eaafd864536b54821dfc44e6bd15b64736f6c63430008180033" +} diff --git a/snapshotter/tests/static/bytecode/uups_upgradeable.json b/snapshotter/tests/static/bytecode/uups_upgradeable.json new file mode 100644 index 0000000..15d9be5 --- /dev/null +++ b/snapshotter/tests/static/bytecode/uups_upgradeable.json @@ -0,0 +1,3 @@ +{ + "bytecode": "60806040526040516103f03803806103f08339810160408190526100229161025e565b61002c8282610033565b5050610341565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610326565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5b8381101561025657818101518382015260200161023e565b50505f910152565b5f806040838503121561026f575f80fd5b82516001600160a01b0381168114610285575f80fd5b60208401519092506001600160401b03808211156102a1575f80fd5b818501915085601f8301126102b4575f80fd5b8151818111156102c6576102c6610228565b604051601f8201601f19908116603f011681019083821181831017156102ee576102ee610228565b81604052828152886020848701011115610306575f80fd5b61031783602083016020880161023c565b80955050505050509250929050565b5f825161033781846020870161023c565b9190910192915050565b60a38061034d5f395ff3fe6080604052600a600c565b005b60186014601a565b6050565b565b5f604b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f80375f80365f845af43d5f803e8080156069573d5ff35b3d5ffdfea26469706673582212203b4bb5e0fd57d8893d3f24c78de2c21e5b5967bf9075bf3cdc52bc2171b2f4a164736f6c63430008180033" +} From 0d485608a807930609b5dc26e722ce207b5f8861 Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 22:08:10 -0400 Subject: [PATCH 10/19] fix: batch_eth_get_balance_on_block_range in rpc helper --- snapshotter/utils/rpc.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index 6a021c7..1a8c001 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -203,7 +203,7 @@ async def init(self): # Initialize rate limiter requests_per_second = self._rpc_settings.rate_limit.requests_per_second - self._rate_limiter = AsyncLimiter(requests_per_second) + self._rate_limiter = AsyncLimiter(requests_per_second, 1) if not self._sync_nodes_initialized: self._logger.debug('Sync nodes not initialized, initializing...') @@ -605,17 +605,25 @@ async def batch_eth_get_balance_on_block_range( response_data = await self._make_rpc_jsonrpc_call(rpc_query) rpc_response = [] - if not isinstance(response_data, list) and response_data is not None and isinstance(response_data, dict): - response_data = [response_data] - for response in response_data: - if 'result' in response: - eth_balance = response['result'] - rpc_response.append(eth_balance) - else: - rpc_response.append(None) + if isinstance(response_data, list): + response = response_data + elif response_data is not None and isinstance(response_data, dict): + response = [response_data] + for result in response: + if 'result' in result: + eth_balance = result['result'] + eth_balance = int(eth_balance, 16) + rpc_response.append(eth_balance) return rpc_response except Exception as e: - raise e + exc = RPCException( + request=rpc_query, + response=None, + underlying_exception=e, + extra_info=f'RPC_BATCH_ETH_GET_BALANCE_ON_BLOCK_RANGE_ERROR: {str(e)}', + ) + self._logger.trace('Error in batch_eth_get_balance_on_block_range, error {}', str(exc)) + raise exc async def batch_eth_call_on_block_range( self, From 09a7d1a8a19cc8423b94b01b7477f9fb2e018e1b Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 22:08:37 -0400 Subject: [PATCH 11/19] chore: update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5cbd367..8dfceb2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ config/*settings.json snapshotter-lite-local-collector config computes -logs \ No newline at end of file +logs +.coverage From b230eaafed56bb137a93730d890acf3934a788b2 Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 22:09:54 -0400 Subject: [PATCH 12/19] chore: update dev dependencies for pytest --- poetry.lock | 184 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 4 ++ 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index a2837e7..0a8bbab 100755 --- a/poetry.lock +++ b/poetry.lock @@ -790,6 +790,93 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.6.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "cytoolz" version = "0.12.3" @@ -1656,6 +1743,17 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] type = ["pytest-mypy"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "jsonschema" version = "4.23.0" @@ -2035,6 +2133,21 @@ files = [ {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, ] +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "protobuf" version = "5.28.2" @@ -2224,6 +2337,64 @@ files = [ {file = "pysha3-1.0.2.tar.gz", hash = "sha256:fe988e73f2ce6d947220624f04d467faf05f1bbdbc64b0a201296bb3af92739e"}, ] +[[package]] +name = "pytest" +version = "8.3.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, + {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + [[package]] name = "pyunormalize" version = "16.0.0" @@ -2632,6 +2803,17 @@ files = [ {file = "timeago-1.0.16-py3-none-any.whl", hash = "sha256:9b8cb2e3102b329f35a04aa4531982d867b093b19481cfbb1dac7845fa2f79b0"}, ] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "toolz" version = "0.12.1" @@ -3128,4 +3310,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "0ee8cfeddf22b2b71b555f067e8d0052c0eb777cdf5b138760204bf3f9481267" +content-hash = "f9ecb99ff2cd7bf75b9f2fc78c451c39d29ec0e29572a3b91a0a47503a937949" diff --git a/pyproject.toml b/pyproject.toml index 462ab88..bdf7130 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,10 @@ grpcio-tools = "^1.62.1" aiolimiter = "^1.1.0" +[tool.poetry.group.dev.dependencies] +pytest-cov = "^5.0.0" +pytest-asyncio = "^0.24.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From f562141271b5469194491cbeb164eeaa72abc494 Mon Sep 17 00:00:00 2001 From: Seth Date: Mon, 30 Sep 2024 22:16:17 -0400 Subject: [PATCH 13/19] chore: update setup.cfg for pytest --- setup.cfg | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/setup.cfg b/setup.cfg index e415a3b..ee6e2f2 100755 --- a/setup.cfg +++ b/setup.cfg @@ -51,16 +51,15 @@ known_third_party = fastapi,pydantic,starlette [tool:pytest] -# Django configuration: -# https://pytest-django.readthedocs.io/en/latest/ -DJANGO_SETTINGS_MODULE = server.settings # Timeout for tests, so they can not take longer # than this amount of seconds. # You should adjust this value to be as low as possible. # Configuration: # https://pypi.org/project/pytest-timeout/ -timeout = 5 + +# NOTE: Removing timeout for now for rate limiting tests and pytest-timeout is not a current dependency +# timeout = 5 # Directories that are not visited by pytest collector: norecursedirs = *.egg .eggs dist build docs .tox .git __pycache__ @@ -72,26 +71,20 @@ addopts = --strict-markers --strict-config --doctest-modules - --fail-on-template-vars - --dup-fixtures # Output: --tb=short # Parallelism: # -n auto # --boxed # Coverage: - --cov=server - --cov=tests + --cov=snapshotter --cov-branch --cov-report=term-missing:skip-covered --cov-report=html - --cov-fail-under=100 - - -[coverage:run] -plugins = - django_coverage_plugin + # --cov-fail-under=100 +markers = + asyncio: mark test as an asyncio coroutine [mypy] # Mypy configuration: From de8226a652b4e683b9a8dd54dc82a2e8be523ce8 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Oct 2024 20:13:38 -0400 Subject: [PATCH 14/19] chore: update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 8dfceb2..988db5f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ config computes logs .coverage +.pytest_cache +.mypy_cache +htmlcov From 764ae18817fbc5f1c384c45b402350092cd552c2 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Oct 2024 21:11:55 -0400 Subject: [PATCH 15/19] chore: add dev chain snapshot and reversion for testing --- snapshotter/tests/rpc_helper_test.py | 30 +++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/snapshotter/tests/rpc_helper_test.py b/snapshotter/tests/rpc_helper_test.py index cfbfcaa..f07365d 100644 --- a/snapshotter/tests/rpc_helper_test.py +++ b/snapshotter/tests/rpc_helper_test.py @@ -37,13 +37,33 @@ ) @async_fixture(scope='module') -async def rpc_helper(): +async def web3(): + w3 = AsyncWeb3(AsyncHTTPProvider('http://127.0.0.1:8545')) + yield w3 + +@async_fixture(scope='module') +async def snapshot(web3: AsyncWeb3): + # Take a snapshot of the current state + snapshot_id = await web3.provider.make_request('evm_snapshot', []) + print(f'Snapshot created with ID: {snapshot_id}') + + yield snapshot_id['result'] + + # Revert to the snapshot after all tests are done + revert_result = await web3.provider.make_request('evm_revert', [snapshot_id['result']]) + print(f'Snapshot revert result: {revert_result}') + + if not revert_result['result']: + raise Exception('Snapshot revert failed') + +@async_fixture(scope='module') +async def rpc_helper(snapshot): helper = RpcHelper(rpc_settings=TEST_RPC_CONFIG) await helper.init() yield helper @async_fixture(scope='module') -async def rpc_helper_override(): +async def rpc_helper_override(snapshot): override_config = TEST_RPC_CONFIG override_config.rate_limit = RATE_LIMIT_OVERRIDE override_helper = RpcHelper(rpc_settings=override_config) @@ -51,11 +71,7 @@ async def rpc_helper_override(): yield override_helper @async_fixture(scope='module') -async def web3(): - yield AsyncWeb3(AsyncHTTPProvider('http://127.0.0.1:8545')) - -@async_fixture(scope='module') -async def protocol_contract(web3: AsyncWeb3): +async def protocol_contract(web3: AsyncWeb3, snapshot): # Load Implementation ABI and Bytecode with open('snapshotter/static/abis/ProtocolContract.json', 'r') as abi_file: implementation_abi = json.load(abi_file) From da738da672cb7594a6ae4bd4cfb2c9c0711e153b Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Oct 2024 21:12:39 -0400 Subject: [PATCH 16/19] chore: add doc notes for test setup instructions --- snapshotter/tests/rpc_helper_test.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/snapshotter/tests/rpc_helper_test.py b/snapshotter/tests/rpc_helper_test.py index f07365d..0bb7663 100644 --- a/snapshotter/tests/rpc_helper_test.py +++ b/snapshotter/tests/rpc_helper_test.py @@ -15,6 +15,30 @@ from snapshotter.utils.rpc import get_event_sig_and_abi from snapshotter.utils.rpc import RpcHelper +""" +RPC Helper Test Suite + +This test suite is designed to test the RpcHelper class, which provides various methods for interacting with Ethereum nodes via RPC calls. + +Requirements: +- A local development Hardhat node is required to run these tests. + +To run the tests: +1. Ensure you have a local Hardhat node running (typically on http://127.0.0.1:8545). +2. Run the tests using Poetry with the following command: + poetry run python -m pytest snapshotter/tests/rpc_helper_test.py + +What these tests cover: +- Initialization of RpcHelper +- Basic RPC calls (e.g., getting current block number, transaction receipts) +- Contract interactions (function calls) +- Batch RPC calls (e.g., getting balances or calling functions across a range of blocks) +- Event log retrieval +- Rate limiting behavior + +Note: These tests use a custom RPC configuration (TEST_RPC_CONFIG) that points to the local Hardhat node. Ensure your local node matches this configuration. +""" + # Custom RPC config for testing with Hardhat RATE_LIMIT_OVERRIDE = RateLimitConfig( From c10e87b6dc349890ab676552b373529316f992b3 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Oct 2024 22:25:49 -0400 Subject: [PATCH 17/19] chore: change rate limiter to a per node instance --- snapshotter/tests/rpc_helper_test.py | 15 +++++++++------ snapshotter/utils/models/settings_model.py | 14 ++++++-------- snapshotter/utils/rpc.py | 21 ++++++++++++--------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/snapshotter/tests/rpc_helper_test.py b/snapshotter/tests/rpc_helper_test.py index 0bb7663..b823563 100644 --- a/snapshotter/tests/rpc_helper_test.py +++ b/snapshotter/tests/rpc_helper_test.py @@ -43,15 +43,18 @@ RATE_LIMIT_OVERRIDE = RateLimitConfig( requests_per_second=1, - requests_per_minute=60, - requests_per_day=86400, ) TEST_RPC_CONFIG = RPCConfigFull( - full_nodes=[RPCNodeConfig(url='http://127.0.0.1:8545')], - archive_nodes=[RPCNodeConfig(url='http://127.0.0.1:8545')], + full_nodes=[ + RPCNodeConfig( + url='http://127.0.0.1:8545', + rate_limit=RateLimitConfig( + requests_per_second=10, + ), + ), + ], connection_limits=settings.rpc.connection_limits, - rate_limit=settings.rpc.rate_limit, semaphore_value=settings.rpc.semaphore_value, retry=settings.rpc.retry, force_archive_blocks=settings.rpc.force_archive_blocks, @@ -89,7 +92,7 @@ async def rpc_helper(snapshot): @async_fixture(scope='module') async def rpc_helper_override(snapshot): override_config = TEST_RPC_CONFIG - override_config.rate_limit = RATE_LIMIT_OVERRIDE + override_config.full_nodes[0].rate_limit = RATE_LIMIT_OVERRIDE override_helper = RpcHelper(rpc_settings=override_config) await override_helper.init() yield override_helper diff --git a/snapshotter/utils/models/settings_model.py b/snapshotter/utils/models/settings_model.py index 9201704..0838e31 100644 --- a/snapshotter/utils/models/settings_model.py +++ b/snapshotter/utils/models/settings_model.py @@ -22,9 +22,15 @@ class CoreAPI(BaseModel): public_rate_limit: str +class RateLimitConfig(BaseModel): + """RPC Rate limit configuration model.""" + requests_per_second: int + + class RPCNodeConfig(BaseModel): """RPC node configuration model.""" url: str + rate_limit: RateLimitConfig class ConnectionLimits(BaseModel): @@ -34,13 +40,6 @@ class ConnectionLimits(BaseModel): keepalive_expiry: int = 300 -class RateLimitConfig(BaseModel): - """RPC Rate limit configuration model.""" - requests_per_second: int - requests_per_minute: int - requests_per_day: int - - class RPCConfigBase(BaseModel): """Base RPC configuration model.""" full_nodes: List[RPCNodeConfig] @@ -56,7 +55,6 @@ class RPCConfigFull(RPCConfigBase): skip_epoch_threshold_blocks: int polling_interval: int semaphore_value: int = 20 - rate_limit: RateLimitConfig class RLimit(BaseModel): diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index 1a8c001..fdb7855 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -152,7 +152,6 @@ def __init__(self, rpc_settings: RPCConfigBase = settings.rpc, archive_mode=Fals self._client = None self._async_transport = None self._semaphore = None - self._rate_limiter = None async def _init_http_clients(self): """ @@ -201,10 +200,6 @@ async def init(self): if not self._initialized: self._semaphore = asyncio.BoundedSemaphore(value=settings.rpc.semaphore_value) - # Initialize rate limiter - requests_per_second = self._rpc_settings.rate_limit.requests_per_second - self._rate_limiter = AsyncLimiter(requests_per_second, 1) - if not self._sync_nodes_initialized: self._logger.debug('Sync nodes not initialized, initializing...') self.sync_init() @@ -251,6 +246,10 @@ def sync_init(self): 'web3_client': Web3(Web3.HTTPProvider(node.url)), 'web3_client_async': None, 'rpc_url': node.url, + 'rate_limiter': AsyncLimiter( + node.rate_limit.requests_per_second, + 1, + ), }, ) except Exception as exc: @@ -302,8 +301,8 @@ def _on_node_exception(self, retry_state: tenacity.RetryCallState): next_node_idx, retry_state.outcome.exception(), ) - async def _rate_limited_call(self, coroutine): - async with self._rate_limiter: + async def _rate_limited_call(self, coroutine, node_idx): + async with self._nodes[node_idx]['rate_limiter']: return await coroutine @acquire_rpc_semaphore @@ -327,7 +326,7 @@ async def f(node_idx): web3_provider = node['web3_client_async'] try: - current_block = await self._rate_limited_call(web3_provider.eth.block_number) + current_block = await self._rate_limited_call(web3_provider.eth.block_number, node_idx) except Exception as e: exc = RPCException( request='get_current_block_number', @@ -369,6 +368,7 @@ async def f(node_idx): try: tx_receipt_details = await self._rate_limited_call( node['web3_client_async'].eth.get_transaction_receipt(tx_hash), + node_idx, ) except Exception as e: exc = RPCException( @@ -411,7 +411,7 @@ async def f(node_idx): web3_provider = node['web3_client_async'] try: - current_block = await self._rate_limited_call(web3_provider.eth.block_number) + current_block = await self._rate_limited_call(web3_provider.eth.block_number, node_idx) except Exception as e: exc = RPCException( request='get_current_block_number', @@ -458,6 +458,7 @@ async def f(node_idx): web3_tasks = [ self._rate_limited_call( contract_obj.functions[task[0]](*task[1]).call(), + node_idx, ) for task in tasks ] response = await asyncio.gather(*web3_tasks) @@ -513,6 +514,7 @@ async def f(node_idx): try: response = await self._rate_limited_call( self._client.post(url=rpc_url, json=rpc_query), + node_idx, ) response_data = response.json() except Exception as e: @@ -833,6 +835,7 @@ async def f(node_idx): try: event_log = await self._rate_limited_call( web3_provider.eth.get_logs(event_log_query), + node_idx, ) codec: ABICodec = web3_provider.codec all_events = [] From b3f4780afd6dda2f8b41e7134db3ccfc9253b571 Mon Sep 17 00:00:00 2001 From: Seth Date: Tue, 1 Oct 2024 23:21:22 -0400 Subject: [PATCH 18/19] chore: add config option and autofill for rate limits --- env.example | 2 ++ snapshotter_autofill.sh | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/env.example b/env.example index fee8c2a..6ede807 100755 --- a/env.example +++ b/env.example @@ -15,6 +15,8 @@ RELAYER_HOST=https://prost1h-relayer-public.powerloom.io NAMESPACE=UNISWAPV2 POWERLOOM_REPORTING_URL=https://nms-testnet-reporting.powerloom.io PROST_CHAIN_ID=11165 +SOURCE_RPC_RATE_LIMIT=10 +PROST_RPC_RATE_LIMIT=10 IPFS_URL= IPFS_API_KEY= IPFS_API_SECRET= diff --git a/snapshotter_autofill.sh b/snapshotter_autofill.sh index 9c4e9cb..741278b 100755 --- a/snapshotter_autofill.sh +++ b/snapshotter_autofill.sh @@ -170,4 +170,15 @@ else fi +# Set default rate limits +SOURCE_RPC_RATE_LIMIT="${SOURCE_RPC_RATE_LIMIT:-10}" +PROST_RPC_RATE_LIMIT="${PROST_RPC_RATE_LIMIT:-10}" + +echo "Using SOURCE RPC Rate Limit: ${SOURCE_RPC_RATE_LIMIT}" +echo "Using PROST RPC Rate Limit: ${PROST_RPC_RATE_LIMIT}" + +sed -i'.backup' "s/\"source-rpc-rate-limit\"/$SOURCE_RPC_RATE_LIMIT/" config/settings.json + +sed -i'.backup' "s/\"prost-rpc-rate-limit\"/$PROST_RPC_RATE_LIMIT/" config/settings.json + echo 'settings has been populated!' From 842054c617fa94213d3cd0017301fdd9e2a4d180 Mon Sep 17 00:00:00 2001 From: Akshay Dahiya Date: Thu, 3 Oct 2024 14:08:28 +0530 Subject: [PATCH 19/19] chore: using worker count to calculate rate limits, semaphore cleanup --- snapshotter/core_api.py | 2 +- snapshotter/processor_distributor.py | 9 +-- snapshotter/snapshotter_id_ping.py | 2 +- snapshotter/system_event_detector.py | 2 +- snapshotter/tests/rpc_helper_test.py | 1 - snapshotter/tests/test_web3_async_provider.py | 2 +- snapshotter/utils/generic_worker.py | 2 +- snapshotter/utils/helper_functions.py | 28 --------- snapshotter/utils/models/settings_model.py | 1 - snapshotter/utils/rpc.py | 49 ++++------------ snapshotter/utils/utility_functions.py | 57 ------------------- 11 files changed, 18 insertions(+), 137 deletions(-) delete mode 100644 snapshotter/utils/utility_functions.py diff --git a/snapshotter/core_api.py b/snapshotter/core_api.py index 4833731..3f42493 100644 --- a/snapshotter/core_api.py +++ b/snapshotter/core_api.py @@ -62,7 +62,7 @@ async def startup_boilerplate(): """ app.state.core_settings = settings app.state.local_user_cache = dict() - app.state.anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) + app.state.anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc, source_node=False) await app.state.anchor_rpc_helper.init() app.state.protocol_state_contract = app.state.anchor_rpc_helper.get_current_node()['web3_client'].eth.contract( address=Web3.to_checksum_address( diff --git a/snapshotter/processor_distributor.py b/snapshotter/processor_distributor.py index 000e459..48305c3 100644 --- a/snapshotter/processor_distributor.py +++ b/snapshotter/processor_distributor.py @@ -195,7 +195,7 @@ async def _init_rpc_helper(self): """ self._rpc_helper = RpcHelper() await self._rpc_helper.init() - self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) + self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc, source_node=False) await self._anchor_rpc_helper.init() async def _init_rabbitmq_connection(self): @@ -1080,15 +1080,8 @@ def run(self) -> None: for signame in [SIGINT, SIGTERM, SIGQUIT]: signal(signame, self._signal_handler) asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - self._anchor_rpc_helper = RpcHelper( - rpc_settings=settings.anchor_chain_rpc, - ) - - self._slots_per_day = 12 - self._logger.debug('Set slots per day to {}', self._slots_per_day) ev_loop = asyncio.get_event_loop() - ev_loop.run_until_complete(self._anchor_rpc_helper.init()) ev_loop.run_until_complete(self.init_worker()) self._logger.debug('Starting RabbitMQ consumer on queue {} for Processor Distributor', self._consume_queue_name) diff --git a/snapshotter/snapshotter_id_ping.py b/snapshotter/snapshotter_id_ping.py index a597eee..a5e9042 100644 --- a/snapshotter/snapshotter_id_ping.py +++ b/snapshotter/snapshotter_id_ping.py @@ -22,7 +22,7 @@ async def main(): redis_conn = aioredis_pool._aioredis_pool # Initialize RPC helper for anchor chain - anchor_rpc = RpcHelper(settings.anchor_chain_rpc) + anchor_rpc = RpcHelper(settings.anchor_chain_rpc, source_node=False) await anchor_rpc.init() # Load protocol state ABI diff --git a/snapshotter/system_event_detector.py b/snapshotter/system_event_detector.py index 139934a..5075ae6 100644 --- a/snapshotter/system_event_detector.py +++ b/snapshotter/system_event_detector.py @@ -117,7 +117,7 @@ def __init__(self, name, **kwargs): self._last_processed_block = None self._source_rpc_helper = RpcHelper(rpc_settings=settings.rpc) - self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) + self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc, source_node=False) self.contract_abi = read_json_file( settings.protocol_state.abi, self._logger, diff --git a/snapshotter/tests/rpc_helper_test.py b/snapshotter/tests/rpc_helper_test.py index b823563..c062bda 100644 --- a/snapshotter/tests/rpc_helper_test.py +++ b/snapshotter/tests/rpc_helper_test.py @@ -55,7 +55,6 @@ ), ], connection_limits=settings.rpc.connection_limits, - semaphore_value=settings.rpc.semaphore_value, retry=settings.rpc.retry, force_archive_blocks=settings.rpc.force_archive_blocks, request_time_out=settings.rpc.request_time_out, diff --git a/snapshotter/tests/test_web3_async_provider.py b/snapshotter/tests/test_web3_async_provider.py index 069a1aa..b09cb67 100644 --- a/snapshotter/tests/test_web3_async_provider.py +++ b/snapshotter/tests/test_web3_async_provider.py @@ -31,7 +31,7 @@ async def test_web3_async_call(): writer_redis_pool = aioredis_pool._aioredis_pool # Set up the RPC helper with the anchor chain RPC - rpc_helper = RpcHelper(settings.anchor_chain_rpc) + rpc_helper = RpcHelper(settings.anchor_chain_rpc, source_node=False) await rpc_helper.init() # Create a synchronous Web3 client diff --git a/snapshotter/utils/generic_worker.py b/snapshotter/utils/generic_worker.py index 8a88374..2d30fe7 100644 --- a/snapshotter/utils/generic_worker.py +++ b/snapshotter/utils/generic_worker.py @@ -499,7 +499,7 @@ async def _init_rpc_helper(self): """ self._rpc_helper = RpcHelper(rpc_settings=settings.rpc) await self._rpc_helper.init() - self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc) + self._anchor_rpc_helper = RpcHelper(rpc_settings=settings.anchor_chain_rpc, source_node=False) await self._anchor_rpc_helper.init() await self._anchor_rpc_helper._load_async_web3_providers() self._protocol_state_contract = self._anchor_rpc_helper.get_current_node()['web3_client'].eth.contract( diff --git a/snapshotter/utils/helper_functions.py b/snapshotter/utils/helper_functions.py index 5b64984..588fa3e 100644 --- a/snapshotter/utils/helper_functions.py +++ b/snapshotter/utils/helper_functions.py @@ -43,34 +43,6 @@ def wrapper(self, *args, **kwargs): return wrapper -def acquire_threading_semaphore(fn): - """ - A decorator function that acquires a threading semaphore before executing the decorated function and releases it after execution. - - Args: - fn (function): The function to be decorated. - - Returns: - function: The decorated function. - """ - @wraps(fn) - def semaphore_wrapper(*args, **kwargs): - semaphore = kwargs['semaphore'] - - logger.debug('Acquiring threading semaphore') - semaphore.acquire() - try: - resp = fn(*args, **kwargs) - except Exception: - raise - finally: - semaphore.release() - - return resp - - return semaphore_wrapper - - def preloading_entry_exit_logger(fn): """ Decorator function to log entry and exit of preloading worker functions. diff --git a/snapshotter/utils/models/settings_model.py b/snapshotter/utils/models/settings_model.py index cea9d84..453f857 100644 --- a/snapshotter/utils/models/settings_model.py +++ b/snapshotter/utils/models/settings_model.py @@ -54,7 +54,6 @@ class RPCConfigFull(RPCConfigBase): """Full RPC configuration model.""" skip_epoch_threshold_blocks: int polling_interval: int - semaphore_value: int = 20 class RLimit(BaseModel): diff --git a/snapshotter/utils/rpc.py b/snapshotter/utils/rpc.py index 2e9e1ab..03ed0c2 100644 --- a/snapshotter/utils/rpc.py +++ b/snapshotter/utils/rpc.py @@ -107,36 +107,9 @@ def get_event_sig_and_abi(event_signatures, event_abis): return event_sig, event_abi -def acquire_rpc_semaphore(fn): - """ - A decorator function that acquires a bounded semaphore before executing the decorated function and releases it - after the function is executed. This decorator is intended to be used with async functions. - - Args: - fn: The async function to be decorated. - - Returns: - The decorated async function. - """ - @wraps(fn) - async def wrapped(self, *args, **kwargs): - sem: asyncio.BoundedSemaphore = self._semaphore - await sem.acquire() - result = None - try: - result = await fn(self, *args, **kwargs) - return result - except Exception as e: - logger.opt(exception=True).error('Error in asyncio semaphore acquisition decorator: {}', e) - raise e - finally: - sem.release() - return wrapped - - class RpcHelper(object): - def __init__(self, rpc_settings: RPCConfigBase = settings.rpc, archive_mode=False): + def __init__(self, rpc_settings: RPCConfigBase = settings.rpc, archive_mode=False, source_node: bool = False): """ Initializes an instance of the RpcHelper class. @@ -154,7 +127,7 @@ def __init__(self, rpc_settings: RPCConfigBase = settings.rpc, archive_mode=Fals self._logger = logger self._client = None self._async_transport = None - self._semaphore = None + self._source_node = source_node async def _init_http_clients(self): """ @@ -201,7 +174,6 @@ async def init(self): None """ if not self._initialized: - self._semaphore = asyncio.BoundedSemaphore(value=settings.rpc.semaphore_value) if not self._sync_nodes_initialized: self._logger.debug('Sync nodes not initialized, initializing...') @@ -244,13 +216,22 @@ def sync_init(self): raise Exception('No full nor archive nodes found in config') for node in nodes: try: + _total_workers = settings.callback_worker_config.num_aggregation_workers + \ + settings.callback_worker_config.num_snapshot_workers + settings.callback_worker_config.num_delegate_workers + 1 + if self._source_node: + # Adding 1 to account for processor distributor RPC usage + _total_workers += 1 + else: + # Adding 3 to account for processor distributor, event detector and + # core API usage (assuming there isn't crazy load and it is serving reasonable amount of requests) + _total_workers += 3 self._nodes.append( { 'web3_client': Web3(Web3.HTTPProvider(node.url)), 'web3_client_async': None, 'rpc_url': node.url, 'rate_limiter': AsyncLimiter( - node.rate_limit.requests_per_second, + max(1, (node.rate_limit.requests_per_second) // (_total_workers)), 1, ), }, @@ -308,7 +289,6 @@ async def _rate_limited_call(self, coroutine, node_idx): async with self._nodes[node_idx]['rate_limiter']: return await coroutine - @acquire_rpc_semaphore async def get_current_block_number(self): """ Returns: @@ -343,7 +323,6 @@ async def f(node_idx): return current_block return await f(node_idx=0) - @acquire_rpc_semaphore async def get_transaction_receipt(self, tx_hash): """ Retrieves the transaction receipt for a given transaction hash. @@ -388,7 +367,6 @@ async def f(node_idx): return tx_receipt_details return await f(node_idx=0) - @acquire_rpc_semaphore async def get_current_block(self, node_idx=0): """ Returns the current block number of the Ethereum blockchain. @@ -428,7 +406,6 @@ async def f(node_idx): return current_block return await f(node_idx=0) - @acquire_rpc_semaphore async def web3_call(self, tasks, contract_addr, abi): """ Calls the given tasks asynchronously using web3 and returns the response. @@ -489,7 +466,6 @@ async def f(node_idx): raise exc return await f(node_idx=0) - @acquire_rpc_semaphore async def _make_rpc_jsonrpc_call(self, rpc_query): """ Makes an RPC JSON-RPC call to a node in the pool. @@ -796,7 +772,6 @@ async def batch_eth_get_block(self, from_block, to_block): response_data = await self._make_rpc_jsonrpc_call(rpc_query) return response_data - @acquire_rpc_semaphore async def get_events_logs( self, contract_address, to_block, from_block, topics, event_abi, ): diff --git a/snapshotter/utils/utility_functions.py b/snapshotter/utils/utility_functions.py deleted file mode 100644 index 3b7f219..0000000 --- a/snapshotter/utils/utility_functions.py +++ /dev/null @@ -1,57 +0,0 @@ -import asyncio -from functools import wraps - -from snapshotter.utils.default_logger import default_logger - - -def acquire_bounded_semaphore(fn): - """ - A decorator function that acquires a bounded semaphore before executing the decorated function and releases it - after the function is executed. This decorator is intended to be used with async functions. - - Args: - fn (callable): The async function to be decorated. - - Returns: - callable: The decorated async function. - - Raises: - Any exception that may occur during the execution of the decorated function. - """ - @wraps(fn) - async def wrapped(self, *args, **kwargs): - """ - Wrapper function that handles semaphore acquisition and release. - - Args: - self: The instance of the class containing the decorated method. - *args: Variable length argument list for the decorated function. - **kwargs: Arbitrary keyword arguments for the decorated function. - - Returns: - Any: The result of the decorated function. - - Raises: - Exception: Any exception that occurs during the execution of the decorated function. - """ - # Extract the semaphore from the keyword arguments - sem: asyncio.BoundedSemaphore = kwargs['semaphore'] - - # Acquire the semaphore - await sem.acquire() - - result = None - try: - # Execute the decorated function - result = await fn(self, *args, **kwargs) - except Exception as e: - # Log any exceptions that occur during execution - default_logger.opt(exception=True).error('Error in asyncio semaphore acquisition decorator: {}', e) - raise # Re-raise the exception after logging - finally: - # Ensure the semaphore is released, even if an exception occurred - sem.release() - - return result - - return wrapped