From 893a84a97dff92e678d6336b0a185f6e374fea42 Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Sun, 1 Oct 2023 22:38:12 +0200 Subject: [PATCH] Internal: rename models to improve consistency (#21) --------- Co-authored-by: nesitor --- src/aleph_vrf/coordinator/vrf.py | 94 ++++++++-------- src/aleph_vrf/exceptions.py | 8 +- src/aleph_vrf/executor/main.py | 98 +++++++++-------- src/aleph_vrf/models.py | 69 ++++++------ tests/coordinator/test_integration_lib.py | 44 ++++---- tests/executor/test_integration.py | 126 +++++++++++----------- tests/malicious_executor.py | 20 ++-- 7 files changed, 235 insertions(+), 224 deletions(-) diff --git a/src/aleph_vrf/coordinator/vrf.py b/src/aleph_vrf/coordinator/vrf.py index d5f151a..f9758fc 100644 --- a/src/aleph_vrf/coordinator/vrf.py +++ b/src/aleph_vrf/coordinator/vrf.py @@ -26,11 +26,11 @@ HashesDoNotMatch, ) from aleph_vrf.models import ( - CRNVRFResponse, + ExecutorVRFResponse, VRFRequest, VRFResponse, - PublishedVRFResponseHash, - PublishedVRFRandomBytes, + PublishedVRFRandomNumberHash, + PublishedVRFRandomNumber, Executor, PublishedVRFResponse, ) @@ -55,7 +55,7 @@ M = TypeVar("M", bound=BaseModel) -async def post_node_vrf(url: str, model: Type[M]) -> M: +async def post_executor_api_request(url: str, model: Type[M]) -> M: async with aiohttp.ClientSession() as session: async with session.post(url, timeout=60) as resp: if resp.status != 200: @@ -99,27 +99,26 @@ async def _generate_vrf( logger.debug(f"Generated VRF request with item_hash {request_item_hash}") - vrf_generated_result = await send_generate_requests( + vrf_generation_results = await send_generate_requests( executors=executors, request_item_hash=request_item_hash, - request_id=vrf_request.request_id, ) logger.debug( - f"Received VRF generated requests from {len(vrf_generated_result)} executors" + f"Received VRF generated requests from {len(vrf_generation_results)} executors" ) - vrf_publish_result = await send_publish_requests(vrf_generated_result) + vrf_publication_results = await send_publish_requests(vrf_generation_results) logger.debug( - f"Received VRF publish requests from {len(vrf_generated_result)} executors" + f"Received VRF publish requests from {len(vrf_generation_results)} executors" ) vrf_response = generate_final_vrf( nb_executors, nonce, - vrf_generated_result, - vrf_publish_result, + vrf_generation_results, + vrf_publication_results, vrf_request, ) @@ -163,64 +162,67 @@ async def generate_vrf( async def send_generate_requests( executors: List[Executor], request_item_hash: ItemHash, - request_id: RequestId, -) -> Dict[Executor, PublishedVRFResponseHash]: +) -> Dict[Executor, PublishedVRFRandomNumberHash]: generate_tasks = [] for executor in executors: url = f"{executor.api_url}/{VRF_FUNCTION_GENERATE_PATH}/{request_item_hash}" generate_tasks.append( - asyncio.create_task(post_node_vrf(url, PublishedVRFResponseHash)) + asyncio.create_task( + post_executor_api_request(url, PublishedVRFRandomNumberHash) + ) ) vrf_generated_responses = await asyncio.gather( *generate_tasks, return_exceptions=True ) - generate_results = dict(zip(executors, vrf_generated_responses)) + generation_results = dict(zip(executors, vrf_generated_responses)) - for executor, result in generate_results.items(): + for executor, result in generation_results.items(): if isinstance(result, Exception): raise RandomNumberGenerationFailed(executor=executor) from result - return generate_results + return generation_results async def send_publish_requests( - vrf_generated_result: Dict[Executor, PublishedVRFResponseHash], -) -> Dict[Executor, PublishedVRFRandomBytes]: + vrf_generation_results: Dict[Executor, PublishedVRFRandomNumberHash], +) -> Dict[Executor, PublishedVRFRandomNumber]: publish_tasks = [] executors: List[Executor] = [] - for executor, vrf_generated_response in vrf_generated_result.items(): + for executor, vrf_random_number_hash in vrf_generation_results.items(): executors.append(executor) - node_message_hash = vrf_generated_response.message_hash - url = f"{executor.api_url}/{VRF_FUNCTION_PUBLISH_PATH}/{node_message_hash}" + executor_message_hash = vrf_random_number_hash.message_hash + url = f"{executor.api_url}/{VRF_FUNCTION_PUBLISH_PATH}/{executor_message_hash}" publish_tasks.append( - asyncio.create_task(post_node_vrf(url, PublishedVRFRandomBytes)) + asyncio.create_task( + post_executor_api_request(url, PublishedVRFRandomNumber) + ) ) vrf_publish_responses = await asyncio.gather(*publish_tasks, return_exceptions=True) - publish_results = dict(zip(executors, vrf_publish_responses)) + publication_results = dict(zip(executors, vrf_publish_responses)) - for executor, result in publish_results.items(): + for executor, result in publication_results.items(): if isinstance(result, Exception): raise RandomNumberPublicationFailed(executor=executor) from result - return publish_results + return publication_results def generate_final_vrf( nb_executors: int, nonce: Nonce, - vrf_generated_result: Dict[Executor, PublishedVRFResponseHash], - vrf_publish_result: Dict[Executor, PublishedVRFRandomBytes], + vrf_generation_results: Dict[Executor, PublishedVRFRandomNumberHash], + vrf_publication_results: Dict[Executor, PublishedVRFRandomNumber], vrf_request: VRFRequest, ) -> VRFResponse: - nodes_responses = [] + executor_responses = [] random_numbers_list = [] - for executor, vrf_publish_response in vrf_publish_result.items(): - if (generation_hash := vrf_generated_result[executor].random_bytes_hash) != ( - publication_hash := vrf_publish_response.random_bytes_hash + for executor, vrf_random_number in vrf_publication_results.items(): + if (generation_hash := vrf_generation_results[executor].random_number_hash) != ( + publication_hash := vrf_random_number.random_number_hash ): raise HashesDoNotMatch( executor=executor, @@ -229,34 +231,34 @@ def generate_final_vrf( ) verified = verify( - binary_to_bytes(vrf_publish_response.random_bytes), + binary_to_bytes(vrf_random_number.random_bytes), nonce, generation_hash, ) if not verified: raise HashValidationFailed( - random_bytes=vrf_publish_response, + random_number=vrf_random_number, random_number_hash=generation_hash, executor=executor, ) random_numbers_list.append( - int_to_bytes(int(vrf_publish_response.random_number), n=vrf_request.nb_bytes) + int_to_bytes(int(vrf_random_number.random_number), n=vrf_request.nb_bytes) ) - node_response = CRNVRFResponse( + executor_response = ExecutorVRFResponse( url=executor.node.address, - execution_id=vrf_publish_response.execution_id, - random_number=str(vrf_publish_response.random_number), - random_bytes=vrf_publish_response.random_bytes, - random_bytes_hash=vrf_generated_result[executor].random_bytes_hash, - generation_message_hash=vrf_generated_result[executor].message_hash, - publish_message_hash=vrf_publish_response.message_hash, + execution_id=vrf_random_number.execution_id, + random_number=str(vrf_random_number.random_number), + random_bytes=vrf_random_number.random_bytes, + random_number_hash=vrf_generation_results[executor].random_number_hash, + generation_message_hash=vrf_generation_results[executor].message_hash, + publication_message_hash=vrf_random_number.message_hash, ) - nodes_responses.append(node_response) + executor_responses.append(executor_response) - final_random_nb_bytes = xor_all(random_numbers_list) - final_random_number = bytes_to_int(final_random_nb_bytes) + final_random_number_bytes = xor_all(random_numbers_list) + final_random_number = bytes_to_int(final_random_number_bytes) return VRFResponse( nb_bytes=vrf_request.nb_bytes, @@ -264,7 +266,7 @@ def generate_final_vrf( nonce=nonce, vrf_function=vrf_request.vrf_function, request_id=vrf_request.request_id, - nodes=nodes_responses, + executors=executor_responses, random_number=str(final_random_number), ) diff --git a/src/aleph_vrf/exceptions.py b/src/aleph_vrf/exceptions.py index 14a4ab5..604b159 100644 --- a/src/aleph_vrf/exceptions.py +++ b/src/aleph_vrf/exceptions.py @@ -1,4 +1,4 @@ -from aleph_vrf.models import Executor, PublishedVRFRandomBytes +from aleph_vrf.models import Executor, PublishedVRFRandomNumber class VrfException(Exception): @@ -79,18 +79,18 @@ class HashValidationFailed(VrfException): def __init__( self, - random_bytes: PublishedVRFRandomBytes, + random_number: PublishedVRFRandomNumber, random_number_hash: str, executor: Executor, ): - self.random_bytes = random_bytes + self.random_number = random_number self.random_number_hash = random_number_hash self.executor = executor def __str__(self): return ( f"The random number published by {self.executor.api_url} " - f"(execution ID: {self.random_bytes.execution_id}) " + f"(execution ID: {self.random_number.execution_id}) " "does not match the hash." ) diff --git a/src/aleph_vrf/executor/main.py b/src/aleph_vrf/executor/main.py index 7bbbdc6..695744b 100644 --- a/src/aleph_vrf/executor/main.py +++ b/src/aleph_vrf/executor/main.py @@ -1,6 +1,7 @@ import logging import sys from typing import Dict, Union, Set +from uuid import uuid4 from aleph_vrf.exceptions import AlephNetworkError @@ -30,12 +31,12 @@ logger.debug("local imports") from aleph_vrf.models import ( APIResponse, - VRFRandomBytes, - VRFResponseHash, - generate_request_from_message, - generate_response_hash_from_message, - PublishedVRFResponseHash, - PublishedVRFRandomBytes, + VRFRandomNumber, + VRFRandomNumberHash, + get_vrf_request_from_message, + get_random_number_hash_from_message, + PublishedVRFRandomNumberHash, + PublishedVRFRandomNumber, ) from aleph_vrf.utils import bytes_to_binary, bytes_to_int, generate @@ -63,7 +64,7 @@ async def authenticated_aleph_client() -> AuthenticatedAlephClient: async def index(): return { "name": "vrf_generate_api", - "endpoints": ["/generate/{vrf_request}", "/publish/{hash_message}"], + "endpoints": ["/generate/{vrf_request_hash}", "/publish/{message_hash}"], } @@ -85,78 +86,85 @@ async def _get_message(client: AlephClient, item_hash: ItemHash) -> PostMessage: ) -@app.post("/generate/{vrf_request}") +@app.post("/generate/{vrf_request_hash}") async def receive_generate( - vrf_request: ItemHash, + vrf_request_hash: ItemHash, aleph_client: Annotated[ AuthenticatedAlephClient, Depends(authenticated_aleph_client) ], -) -> APIResponse[PublishedVRFResponseHash]: +) -> APIResponse[PublishedVRFRandomNumberHash]: """ Generates a random number and returns its SHA3 hash. + + :param vrf_request_hash: Hash of the aleph.im message issued by the coordinator containing the VRF request. + :param aleph_client: Authenticated aleph.im client. """ global SAVED_GENERATED_BYTES, ANSWERED_REQUESTS - message = await _get_message(client=aleph_client, item_hash=vrf_request) - generation_request = generate_request_from_message(message) + message = await _get_message(client=aleph_client, item_hash=vrf_request_hash) + vrf_request = get_vrf_request_from_message(message) + execution_id = ExecutionId(str(uuid4())) - if generation_request.request_id in ANSWERED_REQUESTS: + if vrf_request.request_id in ANSWERED_REQUESTS: raise fastapi.HTTPException( status_code=409, - detail=f"A random number has already been generated for request {vrf_request}", + detail=f"A random number has already been generated for request {vrf_request_hash}", ) generated_bytes, hashed_bytes = generate( - generation_request.nb_bytes, generation_request.nonce + vrf_request.nb_bytes, vrf_request.nonce ) - SAVED_GENERATED_BYTES[generation_request.execution_id] = generated_bytes - ANSWERED_REQUESTS.add(generation_request.request_id) - - response_hash = VRFResponseHash( - nb_bytes=generation_request.nb_bytes, - nonce=generation_request.nonce, - request_id=generation_request.request_id, - execution_id=generation_request.execution_id, - vrf_request=vrf_request, - random_bytes_hash=hashed_bytes, + SAVED_GENERATED_BYTES[execution_id] = generated_bytes + ANSWERED_REQUESTS.add(vrf_request.request_id) + + random_number_hash = VRFRandomNumberHash( + nb_bytes=vrf_request.nb_bytes, + nonce=vrf_request.nonce, + request_id=vrf_request.request_id, + execution_id=execution_id, + vrf_request=vrf_request_hash, + random_number_hash=hashed_bytes, ) ref = ( f"vrf" - f"_{response_hash.request_id}" - f"_{response_hash.execution_id}" + f"_{random_number_hash.request_id}" + f"_{random_number_hash.execution_id}" f"_{GENERATE_MESSAGE_REF_PATH}" ) message_hash = await publish_data( - aleph_client=aleph_client, data=response_hash, ref=ref + aleph_client=aleph_client, data=random_number_hash, ref=ref ) - published_response_hash = PublishedVRFResponseHash.from_vrf_response_hash( - vrf_response_hash=response_hash, message_hash=message_hash + published_random_number_hash = PublishedVRFRandomNumberHash.from_vrf_response_hash( + vrf_response_hash=random_number_hash, message_hash=message_hash ) - return APIResponse(data=published_response_hash) + return APIResponse(data=published_random_number_hash) -@app.post("/publish/{hash_message}") +@app.post("/publish/{message_hash}") async def receive_publish( - hash_message: ItemHash, + message_hash: ItemHash, aleph_client: Annotated[ AuthenticatedAlephClient, Depends(authenticated_aleph_client) ], -) -> APIResponse[PublishedVRFRandomBytes]: +) -> APIResponse[PublishedVRFRandomNumber]: """ Publishes the random number associated with the specified message hash. If a user attempts to call this endpoint several times for the same message hash, data will only be returned on the first call. + + :param message_hash: Hash of the aleph.im message issued by the executor during the generation phase. + :param aleph_client: Authenticated aleph.im client. """ global SAVED_GENERATED_BYTES - message = await _get_message(client=aleph_client, item_hash=hash_message) - response_hash = generate_response_hash_from_message(message) + message = await _get_message(client=aleph_client, item_hash=message_hash) + response_hash = get_random_number_hash_from_message(message) if response_hash.execution_id not in SAVED_GENERATED_BYTES: raise fastapi.HTTPException( @@ -165,34 +173,38 @@ async def receive_publish( random_bytes: bytes = SAVED_GENERATED_BYTES.pop(response_hash.execution_id) - response_bytes = VRFRandomBytes( + random_number = VRFRandomNumber( request_id=response_hash.request_id, execution_id=response_hash.execution_id, vrf_request=response_hash.vrf_request, random_bytes=bytes_to_binary(random_bytes), - random_bytes_hash=response_hash.random_bytes_hash, + random_number_hash=response_hash.random_number_hash, random_number=str(bytes_to_int(random_bytes)), ) ref = f"vrf_{response_hash.request_id}_{response_hash.execution_id}" message_hash = await publish_data( - aleph_client=aleph_client, data=response_bytes, ref=ref + aleph_client=aleph_client, data=random_number, ref=ref ) - published_random_bytes = PublishedVRFRandomBytes.from_vrf_random_bytes( - vrf_random_bytes=response_bytes, message_hash=message_hash + published_random_number = PublishedVRFRandomNumber.from_vrf_random_number( + vrf_random_number=random_number, message_hash=message_hash ) - return APIResponse(data=published_random_bytes) + return APIResponse(data=published_random_number) async def publish_data( aleph_client: AuthenticatedAlephClient, - data: Union[VRFResponseHash, VRFRandomBytes], + data: Union[VRFRandomNumberHash, VRFRandomNumber], ref: str, ) -> ItemHash: """ Publishes the generation/publication artefacts on the aleph.im network as POST messages. + + :param aleph_client: Authenticated aleph.im client. + :param data: Content of the POST message. + :param ref: Reference of the POST message. """ channel = f"vrf_{data.request_id}" diff --git a/src/aleph_vrf/models.py b/src/aleph_vrf/models.py index d31fd10..fb2c730 100644 --- a/src/aleph_vrf/models.py +++ b/src/aleph_vrf/models.py @@ -1,12 +1,11 @@ from typing import List from typing import TypeVar, Generic -from uuid import uuid4 import fastapi from aleph_message.models import ItemHash, PostMessage from aleph_message.models.abstract import HashableModel from pydantic import BaseModel -from pydantic import ValidationError, Field +from pydantic import ValidationError from pydantic.generics import GenericModel from aleph_vrf.types import Nonce, RequestId, ExecutionId @@ -47,18 +46,10 @@ class VRFRequest(BaseModel): node_list_hash: str -class VRFGenerationRequest(BaseModel): - nb_bytes: int - nonce: Nonce - request_id: RequestId - execution_id: ExecutionId = Field(default_factory=lambda: str(uuid4())) - vrf_function: ItemHash - - -def generate_request_from_message(message: PostMessage) -> VRFGenerationRequest: +def get_vrf_request_from_message(message: PostMessage) -> VRFRequest: content = message.content.content try: - return VRFGenerationRequest.parse_obj(content) + return VRFRequest.parse_obj(content) except ValidationError as e: raise fastapi.HTTPException( status_code=422, @@ -66,16 +57,16 @@ def generate_request_from_message(message: PostMessage) -> VRFGenerationRequest: ) -class VRFResponseHash(BaseModel): +class VRFRandomNumberHash(BaseModel): nb_bytes: int nonce: Nonce request_id: RequestId execution_id: ExecutionId vrf_request: ItemHash - random_bytes_hash: str + random_number_hash: str -class PublishedVRFResponseHash(VRFResponseHash): +class PublishedVRFRandomNumberHash(VRFRandomNumberHash): """ A VRF response hash already published on aleph.im. Includes the hash of the message published on aleph.im. @@ -85,71 +76,71 @@ class PublishedVRFResponseHash(VRFResponseHash): @classmethod def from_vrf_response_hash( - cls, vrf_response_hash: VRFResponseHash, message_hash: ItemHash - ) -> "PublishedVRFResponseHash": + cls, vrf_response_hash: VRFRandomNumberHash, message_hash: ItemHash + ) -> "PublishedVRFRandomNumberHash": 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.vrf_request, - random_bytes_hash=vrf_response_hash.random_bytes_hash, + random_number_hash=vrf_response_hash.random_number_hash, message_hash=message_hash, ) -def generate_response_hash_from_message( +def get_random_number_hash_from_message( message: PostMessage, -) -> PublishedVRFResponseHash: +) -> PublishedVRFRandomNumberHash: content = message.content.content try: - response_hash = VRFResponseHash.parse_obj(content) + response_hash = VRFRandomNumberHash.parse_obj(content) except ValidationError as e: raise fastapi.HTTPException( 422, detail=f"Could not parse content of {message.item_hash} as VRF response hash object: {e.json()}", ) - return PublishedVRFResponseHash.from_vrf_response_hash( + return PublishedVRFRandomNumberHash.from_vrf_response_hash( vrf_response_hash=response_hash, message_hash=message.item_hash ) -class VRFRandomBytes(BaseModel): +class VRFRandomNumber(BaseModel): request_id: RequestId execution_id: ExecutionId vrf_request: ItemHash random_bytes: str - random_bytes_hash: str + random_number_hash: str random_number: str -class PublishedVRFRandomBytes(VRFRandomBytes): +class PublishedVRFRandomNumber(VRFRandomNumber): message_hash: ItemHash @classmethod - def from_vrf_random_bytes( - cls, vrf_random_bytes: VRFRandomBytes, message_hash: ItemHash - ) -> "PublishedVRFRandomBytes": + def from_vrf_random_number( + cls, vrf_random_number: VRFRandomNumber, message_hash: ItemHash + ) -> "PublishedVRFRandomNumber": return cls( - request_id=vrf_random_bytes.request_id, - execution_id=vrf_random_bytes.execution_id, - vrf_request=vrf_random_bytes.vrf_request, - random_bytes=vrf_random_bytes.random_bytes, - random_bytes_hash=vrf_random_bytes.random_bytes_hash, - random_number=vrf_random_bytes.random_number, + request_id=vrf_random_number.request_id, + execution_id=vrf_random_number.execution_id, + vrf_request=vrf_random_number.vrf_request, + random_bytes=vrf_random_number.random_bytes, + random_number_hash=vrf_random_number.random_number_hash, + random_number=vrf_random_number.random_number, message_hash=message_hash, ) -class CRNVRFResponse(BaseModel): +class ExecutorVRFResponse(BaseModel): url: str execution_id: ExecutionId random_number: str random_bytes: str - random_bytes_hash: str + random_number_hash: str generation_message_hash: ItemHash - publish_message_hash: ItemHash + publication_message_hash: ItemHash class VRFResponse(BaseModel): @@ -158,7 +149,7 @@ class VRFResponse(BaseModel): nonce: Nonce vrf_function: ItemHash request_id: RequestId - nodes: List[CRNVRFResponse] + executors: List[ExecutorVRFResponse] random_number: str @@ -175,7 +166,7 @@ def from_vrf_response( nonce=vrf_response.nonce, vrf_function=vrf_response.vrf_function, request_id=vrf_response.request_id, - nodes=vrf_response.nodes, + executors=vrf_response.executors, random_number=vrf_response.random_number, message_hash=message_hash, ) diff --git a/tests/coordinator/test_integration_lib.py b/tests/coordinator/test_integration_lib.py index 458a8e4..b70d6a6 100644 --- a/tests/coordinator/test_integration_lib.py +++ b/tests/coordinator/test_integration_lib.py @@ -7,18 +7,21 @@ from aleph_message.models import PostMessage, ItemHash from aleph_vrf.coordinator.executor_selection import UsePredeterminedExecutors -from aleph_vrf.coordinator.vrf import generate_vrf, post_node_vrf +from aleph_vrf.coordinator.vrf import generate_vrf, post_executor_api_request from aleph_vrf.coordinator.vrf import send_generate_requests -from aleph_vrf.exceptions import RandomNumberPublicationFailed, HashValidationFailed, RandomNumberGenerationFailed +from aleph_vrf.exceptions import ( + RandomNumberPublicationFailed, + HashValidationFailed, + RandomNumberGenerationFailed, +) from aleph_vrf.models import ( Executor, Node, VRFResponse, PublishedVRFResponse, - PublishedVRFResponseHash, - PublishedVRFRandomBytes, + PublishedVRFRandomNumberHash, + PublishedVRFRandomNumber, ) -from aleph_vrf.types import RequestId from aleph_vrf.utils import xor_all, bytes_to_int, binary_to_bytes, verify @@ -38,12 +41,12 @@ def assert_vrf_response_equal( assert vrf_response.request_id == expected_vrf_response.request_id assert vrf_response.random_number == expected_vrf_response.random_number - assert len(vrf_response.nodes) == len(expected_vrf_response.nodes) + assert len(vrf_response.executors) == len(expected_vrf_response.executors) expected_nodes_by_execution_id = { - node.execution_id: node for node in expected_vrf_response.nodes + node.execution_id: node for node in expected_vrf_response.executors } - for executor_response in vrf_response.nodes: + for executor_response in vrf_response.executors: expected_executor_response = expected_nodes_by_execution_id[ executor_response.execution_id ] @@ -53,16 +56,16 @@ def assert_vrf_response_equal( executor_response.random_number == expected_executor_response.random_number ) assert ( - executor_response.random_bytes_hash - == expected_executor_response.random_bytes_hash + executor_response.random_number_hash + == expected_executor_response.random_number_hash ) assert ( executor_response.generation_message_hash == expected_executor_response.generation_message_hash ) assert ( - executor_response.publish_message_hash - == expected_executor_response.publish_message_hash + executor_response.publication_message_hash + == expected_executor_response.publication_message_hash ) @@ -107,20 +110,20 @@ async def test_normal_flow( executor_selection_policy=UsePredeterminedExecutors(executors), ) assert vrf_response.nb_executors == nb_executors - assert len(vrf_response.nodes) == nb_executors + assert len(vrf_response.executors) == nb_executors assert vrf_response.nb_bytes == nb_bytes - for executor_response in vrf_response.nodes: + for executor_response in vrf_response.executors: assert verify( random_bytes=binary_to_bytes(executor_response.random_bytes), nonce=vrf_response.nonce, - random_hash=executor_response.random_bytes_hash, + random_hash=executor_response.random_number_hash, ) # Check that we can rebuild the random number using the executor responses random_bytes = [ binary_to_bytes(executor_response.random_bytes) - for executor_response in vrf_response.nodes + for executor_response in vrf_response.executors ] random_number_bytes = xor_all(random_bytes) random_number = bytes_to_int(random_number_bytes) @@ -189,16 +192,15 @@ async def test_malicious_executor( async def send_generate_requests_and_call_publish( executors: List[Executor], request_item_hash: ItemHash, - request_id: RequestId, -) -> Dict[Executor, PublishedVRFResponseHash]: +) -> Dict[Executor, PublishedVRFRandomNumberHash]: generate_response = await send_generate_requests( - executors=executors, request_item_hash=request_item_hash, request_id=request_id + executors=executors, request_item_hash=request_item_hash ) for executor, response in generate_response.items(): - _random_number = await post_node_vrf( + _random_number = await post_executor_api_request( f"{executor.api_url}/publish/{response.message_hash}", - PublishedVRFRandomBytes, + PublishedVRFRandomNumber, ) # We're only interested in one response for this test break diff --git a/tests/executor/test_integration.py b/tests/executor/test_integration.py index 53ad5bb..412ed68 100644 --- a/tests/executor/test_integration.py +++ b/tests/executor/test_integration.py @@ -17,11 +17,11 @@ from aleph_vrf.models import ( VRFRequest, - VRFResponseHash, + VRFRandomNumberHash, VRFResponse, - VRFRandomBytes, - PublishedVRFResponseHash, - PublishedVRFRandomBytes, + VRFRandomNumber, + PublishedVRFRandomNumberHash, + PublishedVRFRandomNumber, ) from aleph_vrf.types import Nonce, RequestId from aleph_vrf.utils import binary_to_bytes, verify @@ -99,87 +99,91 @@ async def published_vrf_request( def assert_vrf_hash_matches_request( - response_hash: PublishedVRFResponseHash, + random_number_hash: PublishedVRFRandomNumberHash, vrf_request: VRFRequest, request_item_hash: ItemHash, ): - assert response_hash.nb_bytes == vrf_request.nb_bytes - assert response_hash.nonce == vrf_request.nonce - assert response_hash.request_id == vrf_request.request_id - assert response_hash.execution_id # This should be a UUID4 - assert response_hash.vrf_request == request_item_hash - assert response_hash.random_bytes_hash + assert random_number_hash.nb_bytes == vrf_request.nb_bytes + assert random_number_hash.nonce == vrf_request.nonce + assert random_number_hash.request_id == vrf_request.request_id + assert random_number_hash.execution_id # This should be a UUID4 + assert random_number_hash.vrf_request == request_item_hash + assert random_number_hash.random_number_hash def assert_random_number_matches_request( - random_bytes: VRFRandomBytes, - response_hash: VRFResponseHash, + random_number: VRFRandomNumber, + random_number_hash: VRFRandomNumberHash, vrf_request: VRFRequest, ): - assert random_bytes.request_id == vrf_request.request_id - assert random_bytes.execution_id == response_hash.execution_id - assert random_bytes.vrf_request == response_hash.vrf_request - assert random_bytes.random_bytes_hash == response_hash.random_bytes_hash + assert random_number.request_id == vrf_request.request_id + assert random_number.execution_id == random_number_hash.execution_id + assert random_number.vrf_request == random_number_hash.vrf_request + assert random_number.random_number_hash == random_number_hash.random_number_hash assert verify( - random_bytes=binary_to_bytes(random_bytes.random_bytes), + random_bytes=binary_to_bytes(random_number.random_bytes), nonce=vrf_request.nonce, - random_hash=response_hash.random_bytes_hash, + random_hash=random_number_hash.random_number_hash, ) -def assert_vrf_response_hash_equal( - response_hash: VRFResponseHash, expected_response_hash: VRFResponseHash +def assert_vrf_random_number_hash_equal( + random_number_hash: VRFRandomNumberHash, + expected_random_number_hash: VRFRandomNumberHash, ): - assert response_hash.nb_bytes == expected_response_hash.nb_bytes - assert response_hash.nonce == expected_response_hash.nonce - assert response_hash.request_id == expected_response_hash.request_id - assert response_hash.execution_id == expected_response_hash.execution_id - assert response_hash.vrf_request == expected_response_hash.vrf_request - assert response_hash.random_bytes_hash == expected_response_hash.random_bytes_hash + assert random_number_hash.nb_bytes == expected_random_number_hash.nb_bytes + assert random_number_hash.nonce == expected_random_number_hash.nonce + assert random_number_hash.request_id == expected_random_number_hash.request_id + assert random_number_hash.execution_id == expected_random_number_hash.execution_id + assert random_number_hash.vrf_request == expected_random_number_hash.vrf_request + assert ( + random_number_hash.random_number_hash + == expected_random_number_hash.random_number_hash + ) # We do not check message_hash as it can be None in the aleph message but set in the API response -async def assert_aleph_message_matches_response_hash( +async def assert_aleph_message_matches_random_number_hash( ccn_url: Any, # aiohttp does not expose its URL type - response_hash: PublishedVRFResponseHash, + random_number_hash: PublishedVRFRandomNumberHash, ) -> PostMessage: - assert response_hash.message_hash + assert random_number_hash.message_hash async with AlephClient(api_server=ccn_url) as client: message = await client.get_message( - response_hash.message_hash, message_type=PostMessage + random_number_hash.message_hash, message_type=PostMessage ) - message_response_hash = VRFResponseHash.parse_obj(message.content.content) - assert_vrf_response_hash_equal(message_response_hash, response_hash) + message_random_number_hash = VRFRandomNumberHash.parse_obj(message.content.content) + assert_vrf_random_number_hash_equal(message_random_number_hash, random_number_hash) return message -def assert_vrf_random_bytes_equal( - random_bytes: VRFRandomBytes, expected_random_bytes: VRFRandomBytes +def assert_vrf_random_number_equal( + random_number: VRFRandomNumber, expected_random_number: VRFRandomNumber ): - assert random_bytes.request_id == expected_random_bytes.request_id - assert random_bytes.execution_id == expected_random_bytes.execution_id - assert random_bytes.vrf_request == expected_random_bytes.vrf_request - assert random_bytes.random_bytes == expected_random_bytes.random_bytes - assert random_bytes.random_bytes_hash == expected_random_bytes.random_bytes_hash - assert random_bytes.random_number == expected_random_bytes.random_number + assert random_number.request_id == expected_random_number.request_id + assert random_number.execution_id == expected_random_number.execution_id + assert random_number.vrf_request == expected_random_number.vrf_request + assert random_number.random_bytes == expected_random_number.random_bytes + assert random_number.random_number_hash == expected_random_number.random_number_hash + assert random_number.random_number == expected_random_number.random_number # We do not check message_hash as it can be None in the aleph message but set in the API response -async def assert_aleph_message_matches_random_bytes( +async def assert_aleph_message_matches_random_number( ccn_url: Any, # aiohttp does not expose its URL type - random_bytes: PublishedVRFRandomBytes, + random_number: PublishedVRFRandomNumber, ) -> PostMessage: async with AlephClient(api_server=ccn_url) as client: message = await client.get_message( - random_bytes.message_hash, message_type=PostMessage + random_number.message_hash, message_type=PostMessage ) - message_random_bytes = VRFRandomBytes.parse_obj(message.content.content) - assert_vrf_random_bytes_equal(message_random_bytes, random_bytes) + message_random_number = VRFRandomNumber.parse_obj(message.content.content) + assert_vrf_random_number_equal(message_random_number, random_number) return message @@ -204,30 +208,30 @@ async def test_normal_request_flow( assert resp.status == 200, await resp.text() response_json = await resp.json() - response_hash = PublishedVRFResponseHash.parse_obj(response_json["data"]) + random_number_hash = PublishedVRFRandomNumberHash.parse_obj(response_json["data"]) - assert_vrf_hash_matches_request(response_hash, vrf_request, item_hash) - random_hash_message = await assert_aleph_message_matches_response_hash( - mock_ccn_client._base_url, response_hash + assert_vrf_hash_matches_request(random_number_hash, vrf_request, item_hash) + random_number_hash_message = await assert_aleph_message_matches_random_number_hash( + mock_ccn_client._base_url, random_number_hash ) - resp = await executor_client.post(f"/publish/{response_hash.message_hash}") + resp = await executor_client.post(f"/publish/{random_number_hash.message_hash}") assert resp.status == 200, await resp.text() response_json = await resp.json() - random_bytes = PublishedVRFRandomBytes.parse_obj(response_json["data"]) + random_number = PublishedVRFRandomNumber.parse_obj(response_json["data"]) assert_random_number_matches_request( - random_bytes=random_bytes, - response_hash=response_hash, + random_number=random_number, + random_number_hash=random_number_hash, vrf_request=vrf_request, ) - random_number_message = await assert_aleph_message_matches_random_bytes( - mock_ccn_client._base_url, random_bytes + random_number_message = await assert_aleph_message_matches_random_number( + mock_ccn_client._base_url, random_number ) # Sanity checks on the message - assert random_number_message.sender == random_hash_message.sender - assert random_number_message.chain == random_hash_message.chain + assert random_number_message.sender == random_number_hash_message.sender + assert random_number_message.chain == random_number_hash_message.chain @pytest.mark.asyncio @@ -247,14 +251,14 @@ async def test_call_publish_twice( assert resp.status == 200, await resp.text() response_json = await resp.json() - response_hash = PublishedVRFResponseHash.parse_obj(response_json["data"]) + random_number_hash = PublishedVRFRandomNumberHash.parse_obj(response_json["data"]) # Call POST /publish a first time - resp = await executor_client.post(f"/publish/{response_hash.message_hash}") + resp = await executor_client.post(f"/publish/{random_number_hash.message_hash}") assert resp.status == 200, await resp.text() # Call it a second time - resp = await executor_client.post(f"/publish/{response_hash.message_hash}") + resp = await executor_client.post(f"/publish/{random_number_hash.message_hash}") assert resp.status == 404, await resp.text() diff --git a/tests/malicious_executor.py b/tests/malicious_executor.py index 789a6c5..7bb6333 100644 --- a/tests/malicious_executor.py +++ b/tests/malicious_executor.py @@ -24,8 +24,8 @@ from aleph_vrf.models import ( APIResponse, - PublishedVRFResponseHash, - PublishedVRFRandomBytes, + PublishedVRFRandomNumberHash, + PublishedVRFRandomNumber, ) from aleph_vrf.utils import bytes_to_binary @@ -34,31 +34,31 @@ app = AlephApp(http_app=http_app) -@app.post("/generate/{vrf_request}") +@app.post("/generate/{vrf_request_hash}") async def receive_generate( - vrf_request: ItemHash, + vrf_request_hash: ItemHash, aleph_client: Annotated[ AuthenticatedAlephClient, Depends(authenticated_aleph_client) ], -) -> APIResponse[PublishedVRFResponseHash]: +) -> APIResponse[PublishedVRFRandomNumberHash]: from aleph_vrf.executor.main import receive_generate as real_receive_generate return await real_receive_generate( - vrf_request=vrf_request, aleph_client=aleph_client + vrf_request_hash=vrf_request_hash, aleph_client=aleph_client ) -@app.post("/publish/{hash_message}") +@app.post("/publish/{message_hash}") async def receive_publish( - hash_message: ItemHash, + message_hash: ItemHash, aleph_client: Annotated[ AuthenticatedAlephClient, Depends(authenticated_aleph_client) ], -) -> APIResponse[PublishedVRFRandomBytes]: +) -> APIResponse[PublishedVRFRandomNumber]: from aleph_vrf.executor.main import receive_publish as real_receive_publish api_response = await real_receive_publish( - hash_message=hash_message, aleph_client=aleph_client + message_hash=message_hash, aleph_client=aleph_client ) # Replace the generated random number with a hardcoded value random_number = 123456789