From 212a664d6f10baf1d8df4e008cfddf83f5d8cb32 Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Fri, 22 Sep 2023 12:23:18 +0200 Subject: [PATCH] done --- mypy.ini | 69 ++++++++++++++++++++++++++++++++ src/aleph_vrf/coordinator/vrf.py | 59 ++++++++++++++++----------- src/aleph_vrf/executor/main.py | 15 ++++--- src/aleph_vrf/models.py | 51 ++++++++++++++++++++--- 4 files changed, 161 insertions(+), 33 deletions(-) create mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..175fc9a --- /dev/null +++ b/mypy.ini @@ -0,0 +1,69 @@ +# Global options: + +[mypy] +mypy_path = src + +exclude = conftest.py + + +show_column_numbers = True + +# Suppressing errors +# Shows errors related to strict None checking, if the global strict_optional flag is enabled +strict_optional = True +no_implicit_optional = True + +# Import discovery +# Suppresses error messages about imports that cannot be resolved +ignore_missing_imports = True +# Forces import to reference the original source file +no_implicit_reexport = True +# show error messages from unrelated files +follow_imports = silent +follow_imports_for_stubs = False + + +# Disallow dynamic typing +# Disallows usage of types that come from unfollowed imports +disallow_any_unimported = False +# Disallows all expressions in the module that have type Any +disallow_any_expr = False +# Disallows functions that have Any in their signature after decorator transformation. +disallow_any_decorated = False +# Disallows explicit Any in type positions such as type annotations and generic type parameters. +disallow_any_explicit = False +# Disallows usage of generic types that do not specify explicit type parameters. +disallow_any_generics = False +# Disallows subclassing a value of type Any. +disallow_subclassing_any = False + +# Untyped definitions and calls +# Disallows calling functions without type annotations from functions with type annotations. +disallow_untyped_calls = False +# Disallows defining functions without type annotations or with incomplete type annotations +disallow_untyped_defs = False +# Disallows defining functions with incomplete type annotations. +check_untyped_defs = False +# Type-checks the interior of functions without type annotations. +disallow_incomplete_defs = False +# Reports an error whenever a function with type annotations is decorated with a decorator without annotations. +disallow_untyped_decorators = False + +# Prohibit comparisons of non-overlapping types (ex: 42 == "no") +strict_equality = True + +# Configuring warnings +# Warns about unneeded # type: ignore comments. +warn_unused_ignores = True +# Shows errors for missing return statements on some execution paths. +warn_no_return = True +# Shows a warning when returning a value with type Any from a function declared with a non- Any return type. +warn_return_any = False + +# Miscellaneous strictness flags +# Allows variables to be redefined with an arbitrary type, as long as the redefinition is in the same block and nesting level as the original definition. +allow_redefinition = True + +# Ignore the imported code from py-libp2p +[mypy-aleph.toolkit.libp2p_stubs.*] +ignore_errors = True diff --git a/src/aleph_vrf/coordinator/vrf.py b/src/aleph_vrf/coordinator/vrf.py index 75c54a7..08084ab 100644 --- a/src/aleph_vrf/coordinator/vrf.py +++ b/src/aleph_vrf/coordinator/vrf.py @@ -22,6 +22,8 @@ VRFRequest, VRFResponse, VRFResponseHash, + PublishedVRFResponseHash, + PublishedVRFRandomBytes, ) from aleph_vrf.settings import settings from aleph_vrf.utils import ( @@ -143,16 +145,16 @@ async def generate_vrf(account: ETHAccount) -> VRFResponse: logger.debug(f"Generated VRF request with item_hash {request_item_hash}") vrf_generated_result = await send_generate_requests( - selected_nodes, request_item_hash + selected_nodes=selected_nodes, + request_item_hash=request_item_hash, + request_id=vrf_request.request_id, ) logger.debug( f"Received VRF generated requests from {len(vrf_generated_result)} nodes" ) - vrf_publish_result = await send_publish_requests( - vrf_generated_result, vrf_request.request_id - ) + vrf_publish_result = await send_publish_requests(vrf_generated_result) logger.debug( f"Received VRF publish requests from {len(vrf_generated_result)} nodes" @@ -178,53 +180,64 @@ async def generate_vrf(account: ETHAccount) -> VRFResponse: async def send_generate_requests( - selected_nodes: List[Node], request_item_hash: str -) -> Dict[str, Union[Exception, VRFResponseHash]]: + selected_nodes: List[Node], + request_item_hash: str, + request_id: str, +) -> Dict[str, PublishedVRFResponseHash]: generate_tasks = [] nodes: List[str] = [] for node in selected_nodes: nodes.append(node.address) url = f"{node.address}/vm/{settings.FUNCTION}/{VRF_FUNCTION_GENERATE_PATH}/{request_item_hash}" - generate_tasks.append(asyncio.create_task(post_node_vrf(url, VRFResponseHash))) + generate_tasks.append( + asyncio.create_task(post_node_vrf(url, PublishedVRFResponseHash)) + ) vrf_generated_responses = await asyncio.gather( *generate_tasks, return_exceptions=True ) - return dict(zip(nodes, vrf_generated_responses)) + generate_results = dict(zip(nodes, vrf_generated_responses)) + for node, result in generate_results.items(): + if isinstance(result, Exception): + raise ValueError( + f"Generate response not found for Node {node} on request_id {request_id}" + ) + + return generate_results async def send_publish_requests( - vrf_generated_result: Dict[str, VRFResponseHash], - request_id: str, -) -> Dict[str, Union[Exception, VRFRandomBytes]]: + vrf_generated_result: Dict[str, PublishedVRFResponseHash], +) -> Dict[str, PublishedVRFRandomBytes]: + publish_tasks = [] nodes: List[str] = [] - node_task_dict = {} - for node, vrf_generated_response in vrf_generated_result.items(): nodes.append(node) - if isinstance(vrf_generated_response, Exception): - raise ValueError( - f"Generate response not found for Node {node} on request_id {request_id}" - ) node_message_hash = vrf_generated_response.message_hash url = ( f"{node}/vm/{settings.FUNCTION}" f"/{VRF_FUNCTION_PUBLISH_PATH}/{node_message_hash}" ) - task = asyncio.create_task(post_node_vrf(url, VRFRandomBytes)) - node_task_dict[node] = task + publish_tasks.append( + asyncio.create_task(post_node_vrf(url, PublishedVRFRandomBytes)) + ) + + vrf_publish_responses = await asyncio.gather(*publish_tasks, return_exceptions=True) + publish_results = dict(zip(nodes, vrf_publish_responses)) + for node, result in publish_results.items(): + if isinstance(result, Exception): + raise ValueError(f"Publish response not found for {node}") - vrf_publish_responses = await asyncio.gather(*noef, return_exceptions=True) - return dict(zip(nodes, vrf_publish_responses)) + return publish_results def generate_final_vrf( nb_executors: int, nonce: int, - vrf_generated_result: Dict[str, Union[Exception, VRFResponseHash]], - vrf_publish_result: Dict[str, Union[Exception, VRFRandomBytes]], + vrf_generated_result: Dict[str, PublishedVRFResponseHash], + vrf_publish_result: Dict[str, PublishedVRFRandomBytes], vrf_request: VRFRequest, ) -> VRFResponse: nodes_responses = [] diff --git a/src/aleph_vrf/executor/main.py b/src/aleph_vrf/executor/main.py index a6d82a4..45991b1 100644 --- a/src/aleph_vrf/executor/main.py +++ b/src/aleph_vrf/executor/main.py @@ -23,6 +23,8 @@ VRFResponseHash, generate_request_from_message, generate_response_hash_from_message, + PublishedVRFResponseHash, + PublishedVRFRandomBytes, ) from aleph_vrf.utils import bytes_to_binary, bytes_to_int, generate @@ -79,9 +81,11 @@ async def receive_generate(vrf_request: str) -> APIResponse: message_hash = await publish_data(response_hash, ref, account) - response_hash.message_hash = message_hash + published_response_hash = PublishedVRFResponseHash.from_vrf_response_hash( + vrf_response_hash=response_hash, message_hash=message_hash + ) - return APIResponse(data=response_hash) + return APIResponse(data=published_response_hash) @app.post("/publish/{hash_message}") @@ -114,10 +118,11 @@ async def receive_publish(hash_message: str) -> APIResponse: ref = f"vrf_{response_hash.request_id}_{response_hash.execution_id}" message_hash = await publish_data(response_bytes, ref, account) + published_random_bytes = PublishedVRFRandomBytes.from_vrf_random_bytes( + vrf_random_bytes=response_bytes, message_hash=message_hash + ) - response_bytes.message_hash = message_hash - - return APIResponse(data=response_bytes) + return APIResponse(data=published_random_bytes) async def publish_data( diff --git a/src/aleph_vrf/models.py b/src/aleph_vrf/models.py index 126b500..0b87ede 100644 --- a/src/aleph_vrf/models.py +++ b/src/aleph_vrf/models.py @@ -46,19 +46,43 @@ class VRFResponseHash(BaseModel): execution_id: str vrf_request: ItemHash random_bytes_hash: str - message_hash: Optional[str] = None -def generate_response_hash_from_message(message: PostMessage) -> VRFResponseHash: +class PublishedVRFResponseHash(VRFResponseHash): + """ + A VRF response hash already published on aleph.im. + Includes the hash of the message published on aleph.im. + """ + + message_hash: ItemHash + + @classmethod + def from_vrf_response_hash( + cls, vrf_response_hash: VRFResponseHash, message_hash: ItemHash + ) -> "PublishedVRFResponseHash": + return cls( + nb_bytes=vrf_response_hash.nb_bytes, + nonce=vrf_response_hash.nonce, + request_id=vrf_response_hash.request_id, + execution_id=vrf_response_hash.execution_id, + vrf_request=vrf_response_hash.request_id, + random_bytes_hash=vrf_response_hash.random_bytes_hash, + message_hash=message_hash, + ) + + +def generate_response_hash_from_message( + message: PostMessage, +) -> PublishedVRFResponseHash: content = message.content.content - return VRFResponseHash( + return PublishedVRFResponseHash( nb_bytes=content["nb_bytes"], nonce=content["nonce"], request_id=content["request_id"], execution_id=content["execution_id"], vrf_request=ItemHash(content["vrf_request"]), random_bytes_hash=content["random_bytes_hash"], - message_hash=content["message_hash"], + message_hash=message.item_hash, ) @@ -69,7 +93,24 @@ class VRFRandomBytes(BaseModel): random_bytes: str random_bytes_hash: str random_number: str - message_hash: Optional[str] = None + + +class PublishedVRFRandomBytes(VRFRandomBytes): + message_hash: ItemHash + + @classmethod + def from_vrf_random_bytes( + cls, vrf_random_bytes: VRFRandomBytes, message_hash: ItemHash + ) -> "PublishedVRFRandomBytes": + return cls( + request_id=vrf_random_bytes.request_id, + execution_id=vrf_random_bytes.execution_id, + vrf_request=vrf_random_bytes.request_id, + random_bytes=vrf_random_bytes.random_bytes, + random_bytes_hash=vrf_random_bytes.random_bytes_hash, + random_number=vrf_random_bytes.random_number, + message_hash=message_hash, + ) class CRNVRFResponse(BaseModel):