From 20f124a5f8186a3739d31d09c61b00e403edab62 Mon Sep 17 00:00:00 2001 From: Karl Preisner Date: Thu, 15 Aug 2024 23:03:09 +0000 Subject: [PATCH 1/3] use new status endpoint for circuit and proof polling during create methods with wait=True --- src/sindri/sindri.py | 108 +++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 24 deletions(-) diff --git a/src/sindri/sindri.py b/src/sindri/sindri.py index f516fef..53b2d08 100644 --- a/src/sindri/sindri.py +++ b/src/sindri/sindri.py @@ -160,6 +160,28 @@ def _get_circuit(self, circuit_id: str, include_verification_key: bool = False) raise Sindri.APIError("Received unexpected type for circuit detail response.") return response_json + def _get_circuit_status( + self, + circuit_id: str, + ) -> str: + """Hit the circuit_status API endpoint and validate the response. Do not print anything. + This may raise `Sindri.APIError` if the response is invalid.""" + response_status_code, response_json = self._hit_api( + "GET", + f"circuit/{circuit_id}/status", + ) + if response_status_code != 200: + raise Sindri.APIError( + f"Unable to fetch circuit_id={circuit_id}." + f" status={response_status_code} response={response_json}" + ) + if not isinstance(response_json, dict): + raise Sindri.APIError("Received unexpected type for circuit status response.") + status = response_json.get("status", "") + if status == "": + raise Sindri.APIError("Received unexpected type for circuit status response.") + return status + def _get_proof( self, proof_id: str, @@ -189,6 +211,28 @@ def _get_proof( raise Sindri.APIError("Received unexpected type for proof detail response.") return response_json + def _get_proof_status( + self, + proof_id: str, + ) -> str: + """Hit the proof_status API endpoint and validate the response. Do not print anything. + This may raise `Sindri.APIError` if the response is invalid.""" + response_status_code, response_json = self._hit_api( + "GET", + f"proof/{proof_id}/status", + ) + if response_status_code != 200: + raise Sindri.APIError( + f"Unable to fetch proof_id={proof_id}." + f" status={response_status_code} response={response_json}" + ) + if not isinstance(response_json, dict): + raise Sindri.APIError("Received unexpected type for proof status response.") + status = response_json.get("status", "") + if status == "": + raise Sindri.APIError("Received unexpected type for proof status response.") + return status + def _get_verbose_1_circuit_detail(self, circuit_detail: dict) -> dict: """Return a slim circuit detail object for printing.""" return { @@ -379,8 +423,9 @@ def create_circuit( ) if not isinstance(response_json, dict): raise Sindri.APIError("Received unexpected type for circuit detail response.") - # Obtain circuit_id from response - circuit_id = response_json.get("circuit_id", "") + # Obtain circuit_id + circuit = response_json + circuit_id = circuit.get("circuit_id", "") if self.verbose_level > 0: print(f" circuit_id: {circuit_id}") @@ -389,20 +434,26 @@ def create_circuit( if self.verbose_level > 0: print("Circuit: Poll until Ready/Failed") for _ in range(self.max_polling_iterations): - circuit = self._get_circuit(circuit_id, include_verification_key=False) - circuit_status = circuit.get("status", "") - if circuit_status == "Failed": - raise Sindri.APIError( - f"Circuit compilation failed." f" error={circuit.get('error', '')}" - ) - if circuit_status == "Ready": + circuit_status = self._get_circuit_status(circuit_id) + if circuit_status in ["Ready", "Failed"]: break time.sleep(self.polling_interval_sec) else: raise Sindri.APIError("Circuit compile polling timed out.") + # Done polling. Fetch the full detail. + circuit = self._get_circuit(circuit_id, include_verification_key=True) + circuit_status = circuit.get("status", "") + if circuit_status == "Failed": + raise Sindri.APIError( + f"Circuit compilation failed." f" error={circuit.get('error', '')}" + ) + if self.verbose_level > 0: - self.get_circuit(circuit_id, include_verification_key=True) + circuit_detail = circuit.copy() + if self.verbose_level == 1: + circuit_detail = self._get_verbose_1_circuit_detail(circuit_detail) + print(f"{pformat(circuit_detail, indent=4)}\n") # Circuit compilation success! return circuit_id @@ -749,8 +800,9 @@ def prove_circuit( ) if not isinstance(response_json, dict): raise Sindri.APIError("Received unexpected type for proof detail response.") - # Obtain proof_id from response - proof_id = response_json.get("proof_id", "") + # Obtain proof_id + proof = response_json + proof_id = proof.get("proof_id", "") if self.verbose_level > 0: print(f" proof_id: {proof_id}") @@ -759,24 +811,32 @@ def prove_circuit( if self.verbose_level > 0: print("Proof: Poll until Ready/Failed") for _ in range(self.max_polling_iterations): - proof = self._get_proof( - proof_id, - include_proof=False, - include_public=False, - include_smart_contract_calldata=False, - include_verification_key=False, - ) - proof_status = proof.get("status", "") - if proof_status == "Failed": - raise Sindri.APIError(f"Prove circuit failed. error={proof.get('error', '')}") - if proof_status == "Ready": + proof_status = self._get_proof_status(proof_id) + if proof_status in ["Ready", "Failed"]: break time.sleep(self.polling_interval_sec) else: raise Sindri.APIError("Prove circuit polling timed out.") + # Done polling. Fetch the full detail. + proof = self._get_proof( + proof_id, + include_proof=True, + include_public=True, + include_smart_contract_calldata=True, + include_verification_key=True, + ) + proof_status = proof.get("status", "") + if proof_status == "Failed": + raise Sindri.APIError( + f"Prove circuit failed." f" error={proof.get('error', '')}" + ) + if self.verbose_level > 0: - self.get_proof(proof_id) + proof_detail = proof.copy() + if self.verbose_level == 1: + proof_detail = self._get_verbose_1_proof_detail(proof_detail) + print(f"{pformat(proof_detail, indent=4)}\n") # Prove circuit success! return proof_id From acffc95666c695fcfa0aed08865da9ab715a2b1b Mon Sep 17 00:00:00 2001 From: Karl Preisner Date: Thu, 15 Aug 2024 23:21:20 +0000 Subject: [PATCH 2/3] add help to readme --- tests/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/README.md b/tests/README.md index 1552a97..8c7c553 100644 --- a/tests/README.md +++ b/tests/README.md @@ -52,3 +52,6 @@ If you are running the Sindri api locally at `~/myproject` with `~/myproject/API ```bash SINDRI_BASE_URL=http://localhost SINDRI_API_KEY=$(cat ~/myproject/API_KEY) pytest ``` + +#### Show standard output during pytest +Add the `-s` flag to `pytest` to print standard output to the terminal for successful tests. \ No newline at end of file From 4588aaa8bfd36bd1ee4ed343d7f9a7fa4f7f79da Mon Sep 17 00:00:00 2001 From: Karl Preisner Date: Wed, 21 Aug 2024 18:12:47 +0000 Subject: [PATCH 3/3] adjust verbose level 1 printouts, make sindri logo smaller --- src/sindri/sindri.py | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/sindri/sindri.py b/src/sindri/sindri.py index 53b2d08..338bd7a 100644 --- a/src/sindri/sindri.py +++ b/src/sindri/sindri.py @@ -236,24 +236,24 @@ def _get_proof_status( def _get_verbose_1_circuit_detail(self, circuit_detail: dict) -> dict: """Return a slim circuit detail object for printing.""" return { + "status": circuit_detail.get("status", None), + "project_name": circuit_detail.get("project_name", None), "circuit_id": circuit_detail.get("circuit_id", None), - "circuit_name": circuit_detail.get("circuit_name", None), + "tags": circuit_detail.get("tags", None), "circuit_type": circuit_detail.get("circuit_type", None), "compute_time": circuit_detail.get("compute_time", None), - "date_created": circuit_detail.get("date_created", None), - "status": circuit_detail.get("status", None), } def _get_verbose_1_proof_detail(self, proof_detail: dict) -> dict: """Return a slim proof detail object for printing.""" return { + "status": proof_detail.get("status", None), + "proof_id": proof_detail.get("proof_id", None), + "project_name": proof_detail.get("project_name", None), "circuit_id": proof_detail.get("circuit_id", None), - "circuit_name": proof_detail.get("circuit_name", None), + "tags": proof_detail.get("tags", None), "circuit_type": proof_detail.get("circuit_type", None), "compute_time": proof_detail.get("compute_time", None), - "date_created": proof_detail.get("date_created", None), - "proof_id": proof_detail.get("proof_id", None), - "status": proof_detail.get("status", None), } def _hit_api(self, method: str, path: str, data=None, files=None) -> tuple[int, dict | list]: @@ -332,19 +332,11 @@ def _print_sindri_logo(self): # https://ascii-generator.site/ 32 columns print( f"""Sindri API Python SDK - {self.version} - .+******************+. - =********************= - .:. -==================- -=**** -=****- - .::-*+==================- - =********************= - .+******************+. - =**+: - :***** - .:::::::::::::::::::::++==- - .***********************+ - .***********************-""" + -++++++- + -+. + +******= + .*- + -*******=""" ) def _set_json_request_headers(self) -> None: @@ -828,9 +820,7 @@ def prove_circuit( ) proof_status = proof.get("status", "") if proof_status == "Failed": - raise Sindri.APIError( - f"Prove circuit failed." f" error={proof.get('error', '')}" - ) + raise Sindri.APIError(f"Prove circuit failed." f" error={proof.get('error', '')}") if self.verbose_level > 0: proof_detail = proof.copy()