From 80efc42cb499cb5948d29812041b613e260a36c5 Mon Sep 17 00:00:00 2001 From: Pengyu Chen Date: Wed, 5 Feb 2020 16:03:06 -0700 Subject: [PATCH 1/3] Add support for the pydevd-pycharm debugger to aca-py and faber-alice demo. To setup in Pycharm or IntelliJ Idea: https://www.jetbrains.com/help/idea/2019.3/run-debug-configuration-python-remote-debug.html Signed-off-by: Pengyu Chen --- .gitignore | 1 + bin/aca-py | 18 ++++++++++++++ demo/run_demo | 53 ++++++++++++++++++++++++++++++++---------- demo/runners/alice.py | 15 ++++++++++++ demo/runners/faber.py | 15 ++++++++++++ docker/Dockerfile.demo | 2 ++ requirements.dev.txt | 1 + 7 files changed, 93 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index f78373d767..2ba41336e2 100644 --- a/.gitignore +++ b/.gitignore @@ -185,3 +185,4 @@ $RECYCLE.BIN/ # Docs build _build/ +**/*.iml diff --git a/bin/aca-py b/bin/aca-py index 6f37514a44..c08cd68050 100755 --- a/bin/aca-py +++ b/bin/aca-py @@ -3,9 +3,16 @@ import os import sys +import pydevd_pycharm + ENABLE_PTVSD = os.getenv("ENABLE_PTVSD", "").lower() ENABLE_PTVSD = ENABLE_PTVSD and ENABLE_PTVSD not in ("false", "0") +ENABLE_PYDEVD_PYCHARM = os.getenv("ENABLE_PYDEVD_PYCHARM", "").lower() +ENABLE_PYDEVD_PYCHARM = ENABLE_PYDEVD_PYCHARM and ENABLE_PYDEVD_PYCHARM not in ("false", "0") +PYDEVD_PYCHARM_HOST = os.getenv("PYDEVD_PYCHARM_HOST", "localhost") +PYDEVD_PYCHARM_AGENT_PORT = int(os.getenv("PYDEVD_PYCHARM_AGENT_PORT", 5001)) + # --debug-vs to use microsoft's visual studio remote debugger if ENABLE_PTVSD or "--debug" in sys.argv: try: @@ -20,6 +27,17 @@ if ENABLE_PTVSD or "--debug" in sys.argv: print("ptvsd library was not found") +if ENABLE_PYDEVD_PYCHARM or "--debug-pycharm" in sys.argv: + try: + import pydevd_pycharm + + print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_AGENT_PORT}") + pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_AGENT_PORT, + stdoutToServer=True, stderrToServer=True, suspend=False) + except ImportError: + print("pydevd_pycharm library was not found") + + from aries_cloudagent.commands import run_command # noqa if len(sys.argv) > 1 and sys.argv[1] and sys.argv[1][0] != "-": diff --git a/demo/run_demo b/demo/run_demo index a877007998..8278b391a9 100755 --- a/demo/run_demo +++ b/demo/run_demo @@ -7,16 +7,41 @@ cd $(dirname $0) AGENT="$1" shift -case "$@" in *--timing*) - if [ ! -d "../logs" ]; then - mkdir ../logs && chmod -R uga+rws ../logs - fi - if [ "$(ls -ld ../logs | grep dr..r..rwx)" == "" ]; then - echo "Error: To use the --timing parameter, the directory '../logs' must exist and all users must be able to write to it." - echo "For example, to create the directory and then set the permissions use: 'mkdir ../logs; chmod uga+rws ../logs'" - exit 1 - fi -esac +while (( "$#" )); do + case "$1" in + *--timing*) + if [ ! -d "../logs" ]; then + mkdir ../logs && chmod -R uga+rws ../logs + fi + if [ "$(ls -ld ../logs | grep dr..r..rwx)" == "" ]; then + echo "Error: To use the --timing parameter, the directory '../logs' must exist and all users must be able to write to it." + echo "For example, to create the directory and then set the permissions use: 'mkdir ../logs; chmod uga+rws ../logs'" + exit 1 + fi + shift + ;; + --debug-ptvsd) + ENABLE_PTVSD=1 + shift + ;; + --debug-pycharm) + ENABLE_PYDEVD_PYCHARM=1 + shift + ;; + --debug-pycharm-controller-port) + PYDEVD_PYCHARM_CONTROLLER_PORT=$2 + shift 2 + ;; + --debug-pycharm-agent-port) + PYDEVD_PYCHARM_AGENT_PORT=$2 + shift 2 + ;; + *) + PARAMS="$PARAMS $1" + shift + ;; + esac +done if [ "$AGENT" = "faber" ]; then AGENT_MODULE="faber" @@ -75,15 +100,19 @@ if ! [ -z "$GENESIS_URL" ]; then DOCKER_ENV="${DOCKER_ENV} -e GENESIS_URL=${GENESIS_URL}" fi +if ! [[ -z "${ENABLE_PYDEVD_PYCHARM}" ]]; then + DOCKER_ENV="${DOCKER_ENV} -e ENABLE_PYDEVD_PYCHARM=${ENABLE_PYDEVD_PYCHARM} -e PYDEVD_PYCHARM_CONTROLLER_PORT=${PYDEVD_PYCHARM_CONTROLLER_PORT} -e PYDEVD_PYCHARM_AGENT_PORT=${PYDEVD_PYCHARM_AGENT_PORT}" +fi + # on Windows, docker run needs to be prefixed by winpty if [ "$OSTYPE" = "msys" ]; then DOCKER="winpty docker" fi DOCKER=${DOCKER:-docker} -echo "Starting $AGENT..." +echo "Starting $AGENT... $DOCKER_ENV" $DOCKER run --name $AGENT --rm -it \ -p 0.0.0.0:$AGENT_PORT_RANGE:$AGENT_PORT_RANGE \ -v `pwd`/../logs:/home/indy/logs \ $DOCKER_ENV \ - faber-alice-demo $AGENT_MODULE --port $AGENT_PORT $@ + faber-alice-demo $AGENT_MODULE --port $AGENT_PORT $PARAMS diff --git a/demo/runners/alice.py b/demo/runners/alice.py index 847dd186a0..c3f15cff3d 100644 --- a/demo/runners/alice.py +++ b/demo/runners/alice.py @@ -284,6 +284,21 @@ async def main(start_port: int, show_timing: bool = False): ) args = parser.parse_args() + ENABLE_PYDEVD_PYCHARM = os.getenv("ENABLE_PYDEVD_PYCHARM", "").lower() + ENABLE_PYDEVD_PYCHARM = ENABLE_PYDEVD_PYCHARM and ENABLE_PYDEVD_PYCHARM not in ("false", "0") + PYDEVD_PYCHARM_HOST = os.getenv("PYDEVD_PYCHARM_HOST", "localhost") + PYDEVD_PYCHARM_CONTROLLER_PORT = int(os.getenv("PYDEVD_PYCHARM_CONTROLLER_PORT", 5001)) + + if ENABLE_PYDEVD_PYCHARM: + try: + import pydevd_pycharm + + print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") + pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_CONTROLLER_PORT, + stdoutToServer=True, stderrToServer=True, suspend=False) + except ImportError: + print("pydevd_pycharm library was not found") + require_indy() try: diff --git a/demo/runners/faber.py b/demo/runners/faber.py index 7dd8131147..ef97968f51 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -287,6 +287,21 @@ async def main(start_port: int, show_timing: bool = False): ) args = parser.parse_args() + ENABLE_PYDEVD_PYCHARM = os.getenv("ENABLE_PYDEVD_PYCHARM", "").lower() + ENABLE_PYDEVD_PYCHARM = ENABLE_PYDEVD_PYCHARM and ENABLE_PYDEVD_PYCHARM not in ("false", "0") + PYDEVD_PYCHARM_HOST = os.getenv("PYDEVD_PYCHARM_HOST", "localhost") + PYDEVD_PYCHARM_CONTROLLER_PORT = int(os.getenv("PYDEVD_PYCHARM_CONTROLLER_PORT", 5001)) + + if ENABLE_PYDEVD_PYCHARM: + try: + import pydevd_pycharm + + print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") + pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_CONTROLLER_PORT, + stdoutToServer=True, stderrToServer=True, suspend=False) + except ImportError: + print("pydevd_pycharm library was not found") + require_indy() try: diff --git a/docker/Dockerfile.demo b/docker/Dockerfile.demo index aa12dedb71..421cd41752 100644 --- a/docker/Dockerfile.demo +++ b/docker/Dockerfile.demo @@ -1,6 +1,8 @@ FROM bcgovimages/von-image:py36-1.11-1 ENV ENABLE_PTVSD 0 +ENV ENABLE_PYDEVD_PYCHARM 0 +ENV PYDEVD_PYCHARM_HOST "host.docker.internal" # Add and install Indy Agent code ADD requirements*.txt ./ diff --git a/requirements.dev.txt b/requirements.dev.txt index a2c335f469..48c9cd3b08 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -16,3 +16,4 @@ sphinx-rtd-theme>=0.4.3 ptvsd==4.3.2 pydevd==1.5.1 +pydevd-pycharm~=193.6015.39 \ No newline at end of file From 3b0a0cd927efc4a7808331fa78ea0757213b6a90 Mon Sep 17 00:00:00 2001 From: Pengyu Chen Date: Tue, 11 Feb 2020 10:37:03 -0700 Subject: [PATCH 2/3] Remove the extra import Signed-off-by: Pengyu Chen --- bin/aca-py | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/aca-py b/bin/aca-py index c08cd68050..27639200d0 100755 --- a/bin/aca-py +++ b/bin/aca-py @@ -3,7 +3,6 @@ import os import sys -import pydevd_pycharm ENABLE_PTVSD = os.getenv("ENABLE_PTVSD", "").lower() ENABLE_PTVSD = ENABLE_PTVSD and ENABLE_PTVSD not in ("false", "0") From 831a7984c1f06d009cfbab5882ac582b8a6b7d5d Mon Sep 17 00:00:00 2001 From: Pengyu Chen Date: Tue, 11 Feb 2020 15:56:09 -0700 Subject: [PATCH 3/3] Publish revocation registry with faber-alice demo change to use them Signed-off-by: Pengyu Chen --- aries_cloudagent/ledger/base.py | 34 +++- .../protocols/issue_credential/v1_0/routes.py | 13 +- .../models/issuer_revocation_record.py | 4 + aries_cloudagent/revocation/routes.py | 149 +++++++++++++++--- bin/aca-py | 2 +- demo/runners/alice.py | 2 +- demo/runners/faber.py | 10 +- demo/runners/support/agent.py | 66 +++++++- 8 files changed, 243 insertions(+), 37 deletions(-) diff --git a/aries_cloudagent/ledger/base.py b/aries_cloudagent/ledger/base.py index 63c586f1f6..7468b26501 100644 --- a/aries_cloudagent/ledger/base.py +++ b/aries_cloudagent/ledger/base.py @@ -1,10 +1,11 @@ """Ledger base class.""" -from abc import ABC, abstractmethod +from abc import ABC, abstractmethod, ABCMeta import re +from typing import Tuple, Sequence -class BaseLedger(ABC): +class BaseLedger(ABC, metaclass=ABCMeta): """Base class for ledger.""" LEDGER_TYPE = None @@ -86,3 +87,32 @@ async def get_latest_txn_author_acceptance(self): def taa_digest(self, version: str, text: str): """Generate the digest of a TAA record.""" + + @abstractmethod + async def create_and_send_schema( + self, schema_name: str, schema_version: str, attribute_names: Sequence[str] + ) -> Tuple[str, dict]: + """ + Send schema to ledger. + + Args: + schema_name: The schema name + schema_version: The schema version + attribute_names: A list of schema attributes + + """ + + @abstractmethod + def get_revoc_reg_def(self, revoc_reg_id): + """Look up a revocation registry definition by ID.""" + pass + + @abstractmethod + def send_revoc_reg_def(self, revoc_reg_def, issuer_did): + """Publish a revocation registry definition to the ledger.""" + pass + + @abstractmethod + def send_revoc_reg_entry(self, revoc_reg_id, revoc_def_type, revoc_reg_entry, issuer_did): + """Publish a revocation registry entry to the ledger.""" + pass \ No newline at end of file diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index ee918418e0..57deb17f06 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -685,10 +685,13 @@ async def credential_exchange_revoke(request: web.BaseRequest): context = request.app["request_context"] - credential_exchange_id = request.match_info["id"] - credential_exchange_record = await V10CredentialExchange.retrieve_by_id( - context, credential_exchange_id - ) + try: + credential_exchange_id = request.match_info["cred_ex_id"] + credential_exchange_record = await V10CredentialExchange.retrieve_by_id( + context, credential_exchange_id + ) + except StorageNotFoundError: + raise web.HTTPNotFound() if ( credential_exchange_record.state @@ -764,7 +767,7 @@ async def register(app: web.Application): credential_exchange_store, ), web.post( - "/issue-credential/records/{id}/revoke", credential_exchange_revoke + "/issue-credential/records/{cred_ex_id}/revoke", credential_exchange_revoke ), web.post( "/issue-credential/records/{cred_ex_id}/problem-report", diff --git a/aries_cloudagent/revocation/models/issuer_revocation_record.py b/aries_cloudagent/revocation/models/issuer_revocation_record.py index ee719cb051..8aff0ece5e 100644 --- a/aries_cloudagent/revocation/models/issuer_revocation_record.py +++ b/aries_cloudagent/revocation/models/issuer_revocation_record.py @@ -152,6 +152,10 @@ async def generate_registry(self, context: InjectionContext, base_dir: str): self.tails_local_path = self.revoc_reg_def["value"]["tailsLocation"] await self.save(context, reason="Generated registry") + def set_tail_file_public_uri(self, tail_file_uri): + self.tails_public_uri = tail_file_uri + self.revoc_reg_def["value"]["tailsLocation"] = tail_file_uri + async def publish_registry_definition(self, context: InjectionContext): """Send the revocation registry definition to the ledger.""" ledger: BaseLedger = await context.inject(BaseLedger) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 113d363b63..cf47bb984d 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -24,6 +24,10 @@ class RevRegCreateRequestSchema(Schema): description="Credential definition identifier", **INDY_CRED_DEF_ID ) + max_cred_num = fields.Int( + description="Maximum credential numbers", required=False + ) + class RevRegCreateResultSchema(Schema): """Result schema for revocation registry creation request.""" @@ -31,6 +35,14 @@ class RevRegCreateResultSchema(Schema): result = IssuerRevocationRecordSchema() +class RevRegUpdateTailFileUriSchema(Schema): + """Request schema for updating tail file URI.""" + + tails_public_uri = fields.Url( + description="Public URI to the tail file", required=True + ) + + @docs(tags=["revocation"], summary="Creates a new revocation registry") @request_schema(RevRegCreateRequestSchema()) @response_schema(RevRegCreateResultSchema(), 200) @@ -50,6 +62,7 @@ async def revocation_create_registry(request: web.BaseRequest): body = await request.json() credential_definition_id = body.get("credential_definition_id") + max_cred_num = body.get("max_cred_num") # check we published this cred def storage = await context.inject(BaseStorage) @@ -64,7 +77,7 @@ async def revocation_create_registry(request: web.BaseRequest): issuer_did = credential_definition_id.split(":")[0] revoc = IndyRevocation(context) registry_record = await revoc.init_issuer_registry( - credential_definition_id, issuer_did + credential_definition_id, issuer_did, max_cred_num=max_cred_num ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest() from e @@ -75,8 +88,12 @@ async def revocation_create_registry(request: web.BaseRequest): return web.json_response({"result": registry_record.serialize()}) -@docs(tags=["revocation"], summary="Get current revocation registry") -@request_schema(RevRegCreateRequestSchema()) +@docs(tags=["revocation"], summary="Get current revocation registry", + parameters=[{ + "in": "path", + "name": "id", + "description": "use credential definition id as the revocation registry id." + }]) @response_schema(RevRegCreateResultSchema(), 200) async def get_current_registry(request: web.BaseRequest): """ @@ -91,9 +108,7 @@ async def get_current_registry(request: web.BaseRequest): """ context = request.app["request_context"] - body = await request.json() - - credential_definition_id = body.get("credential_definition_id") + credential_definition_id = request.match_info["id"] # check we published this cred def storage = await context.inject(BaseStorage) @@ -114,9 +129,62 @@ async def get_current_registry(request: web.BaseRequest): return web.json_response({"result": registry_record.serialize()}) +@docs(tags=["revocation"], summary="Get the tail file of revocation registry", + produces="application/octet-stream", + parameters=[{ + "in": "path", + "name": "id", + "description": "use credential definition id as the revocation registry id." + }], + responses={ + 200: { + "description": "tail file", + "schema": { + "type": "file" + } + } + }) +async def get_tail_file(request: web.BaseRequest) -> web.FileResponse: + """ + Request handler for getting the tail file of the revocation registry. -@docs(tags=["revocation"], summary="Publish a given revocation registry") -@request_schema(RevRegCreateRequestSchema()) + Args: + request: aiohttp request object + + Returns: + The tail file in FileResponse + + """ + context = request.app["request_context"] + + credential_definition_id = request.match_info["id"] + + # check we published this cred def + storage = await context.inject(BaseStorage) + found = await storage.search_records( + type_filter=CRED_DEF_SENT_RECORD_TYPE, + tag_query={"cred_def_id": credential_definition_id}, + ).fetch_all() + if not found: + raise web.HTTPNotFound() + + try: + revoc = IndyRevocation(context) + registry_record = await revoc.get_active_issuer_revocation_record( + credential_definition_id + ) + except RevocationNotSupportedError as e: + raise web.HTTPBadRequest() from e + + return web.FileResponse(path=registry_record.tails_local_path, status=200) + + +@docs(tags=["revocation"], summary="Publish a given revocation registry", + parameters=[{ + "in": "path", + "name": "id", + "description": "use credential definition id as the revocation registry id." + }]) @response_schema(RevRegCreateResultSchema(), 200) async def publish_registry(request: web.BaseRequest): """ @@ -126,16 +194,12 @@ async def publish_registry(request: web.BaseRequest): request: aiohttp request object Returns: - The revocation registry identifier + The revocation registry record """ context = request.app["request_context"] - registry_id = request.match_info["id"] - - body = await request.json() - - credential_definition_id = body.get("credential_definition_id") + credential_definition_id = request.match_info["id"] # check we published this cred def storage = await context.inject(BaseStorage) @@ -147,27 +211,66 @@ async def publish_registry(request: web.BaseRequest): raise web.HTTPNotFound() try: - revoc = IndyRevocation(context) - registry_record = await revoc.get_ledger_registry(registry_id) + revoc_registry = await IndyRevocation(context).get_active_issuer_revocation_record(credential_definition_id) except RevocationNotSupportedError as e: raise web.HTTPBadRequest() from e - print("generated tails file") - # print(registry_record) - await registry_record.publish_registry_definition(context) + await revoc_registry.publish_registry_definition(context) print("published registry definition") - await registry_record.publish_registry_entry(context) + await revoc_registry.publish_registry_entry(context) print("published registry entry") - return web.json_response({"result": registry_record.serialize()}) + return web.json_response({"result": revoc_registry.serialize()}) + + +@docs(tags=["revocation"], summary="Update revocation registry with new public URI to the tail file.", + parameters=[{ + "in": "path", + "name": "id", + "description": "use credential definition id as the revocation registry id." + }]) +@request_schema(RevRegUpdateTailFileUriSchema()) +@response_schema(RevRegCreateResultSchema(), 200) +async def update_registry(request: web.BaseRequest): + """ + Request handler for updating a revocation registry based on the registry id. + + Args: + request: aiohttp request object + + Returns: + The revocation registry record + + """ + context = request.app["request_context"] + + credential_definition_id = request.match_info["id"] + + body = await request.json() + + tails_public_uri = body.get("tails_public_uri") + + try: + revoc = IndyRevocation(context) + registry_record = await revoc.get_active_issuer_revocation_record( + credential_definition_id + ) + except RevocationNotSupportedError as e: + raise web.HTTPBadRequest() from e + registry_record.set_tail_file_public_uri(tails_public_uri) + await registry_record.save(context, reason="Updating tail file public URI.") + + return web.json_response({"result": registry_record.serialize()}) async def register(app: web.Application): """Register routes.""" app.add_routes( [ web.post("/revocation/create-registry", revocation_create_registry), - web.post("/revocation/get-current-registry", get_current_registry), - web.post("/revocation/records/{id}/publish", publish_registry), + web.get("/revocation/registry/{id}", get_current_registry), + web.get("/revocation/registry/{id}/tail-file", get_tail_file), + web.patch("/revocation/registry/{id}", update_registry), + web.post("/revocation/registry/{id}/publish", publish_registry), ] ) diff --git a/bin/aca-py b/bin/aca-py index 27639200d0..183a112886 100755 --- a/bin/aca-py +++ b/bin/aca-py @@ -30,7 +30,7 @@ if ENABLE_PYDEVD_PYCHARM or "--debug-pycharm" in sys.argv: try: import pydevd_pycharm - print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_AGENT_PORT}") + print(f"aca-py remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_AGENT_PORT}") pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_AGENT_PORT, stdoutToServer=True, stderrToServer=True, suspend=False) except ImportError: diff --git a/demo/runners/alice.py b/demo/runners/alice.py index c3f15cff3d..4fa6a4f5ec 100644 --- a/demo/runners/alice.py +++ b/demo/runners/alice.py @@ -293,7 +293,7 @@ async def main(start_port: int, show_timing: bool = False): try: import pydevd_pycharm - print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") + print(f"Alice remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_CONTROLLER_PORT, stdoutToServer=True, stderrToServer=True, suspend=False) except ImportError: diff --git a/demo/runners/faber.py b/demo/runners/faber.py index ef97968f51..0a6cbf9cea 100644 --- a/demo/runners/faber.py +++ b/demo/runners/faber.py @@ -154,12 +154,18 @@ async def main(start_port: int, show_timing: bool = False): "degree schema", version, ["name", "date", "degree", "age"] ) + with log_timer("Publish revocation registry duration:"): + log_status("#5/6 Create and publish the revocation registry on the ledger") + revocation_registry_id = await agent.create_and_publish_revocation_registry( + credential_definition_id, 2 + ) + # TODO add an additional credential for Student ID with log_timer("Generate invitation duration:"): # Generate an invitation log_status( - "#5 Create a connection to alice and print out the invite details" + "#7 Create a connection to alice and print out the invite details" ) connection = await agent.admin_POST("/connections/create-invitation") @@ -296,7 +302,7 @@ async def main(start_port: int, show_timing: bool = False): try: import pydevd_pycharm - print(f"pydevd_pycharm remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") + print(f"Faber remote debugging to {PYDEVD_PYCHARM_HOST}:{PYDEVD_PYCHARM_CONTROLLER_PORT}") pydevd_pycharm.settrace(host=PYDEVD_PYCHARM_HOST, port=PYDEVD_PYCHARM_CONTROLLER_PORT, stdoutToServer=True, stderrToServer=True, suspend=False) except ImportError: diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 4765273136..692c369ba5 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -6,6 +6,8 @@ import os import random import subprocess +import hashlib +import base58 from timeit import default_timer from aiohttp import ( @@ -133,7 +135,7 @@ def __init__( self.did = None self.wallet_stats = [] - async def register_schema_and_creddef(self, schema_name, version, schema_attrs): + async def register_schema_and_creddef(self, schema_name, version, schema_attrs, support_revocation: bool = False): # Create a schema schema_body = { "schema_name": schema_name, @@ -146,7 +148,10 @@ async def register_schema_and_creddef(self, schema_name, version, schema_attrs): log_msg("Schema ID:", schema_id) # Create a cred def for the schema - credential_definition_body = {"schema_id": schema_id} + credential_definition_body = { + "schema_id": schema_id, + "support_revocation": support_revocation + } credential_definition_response = await self.admin_POST( "/credential-definitions", credential_definition_body ) @@ -154,8 +159,46 @@ async def register_schema_and_creddef(self, schema_name, version, schema_attrs): "credential_definition_id" ] log_msg("Cred def ID:", credential_definition_id) + return schema_id, credential_definition_id + + async def create_and_publish_revocation_registry(self, credential_def_id, max_cred_num): + revoc_response = await self.admin_POST( + "/revocation/create-registry", + { + "credential_definition_id": credential_def_id, + "max_cred_num": max_cred_num + } + ) + revocation_registry_id = revoc_response["result"]["revoc_reg_id"] + tails_hash = revoc_response["result"]["tails_hash"] + + # get the tail file from "GET /revocation/registry/{id}/tail-file" + tail_file = await self.admin_GET_FILE(f"/revocation/registry/{credential_def_id}/tail-file") + hasher = hashlib.sha256() + hasher.update(tail_file) + my_tails_hash = base58.b58encode(hasher.digest()).decode("utf-8") + log_msg(f"Revocation Registry ID: {revocation_registry_id}") + assert tails_hash == my_tails_hash + + # Real app should publish tail file somewhere and update the revocation registry with the URI. + # But for the demo, assume the agent's admin end-points are accessible to the other agents + # Update the revocation registry with the public URL to the tail file + tail_file_url = f"{self.admin_url}/revocation/registry/{credential_def_id}/tail-file" + revoc_updated_response = await self.admin_PATCH( + f"/revocation/registry/{credential_def_id}", + { + "tails_public_uri": tail_file_url + } + ) + tail_public_uri = revoc_updated_response["result"]["tails_public_uri"] + log_msg(f"Revocation Registry Tail File URL: {tail_public_uri}") + assert tail_public_uri == tail_file_url - return (schema_id, credential_definition_id) + revoc_publish_response = await self.admin_POST( + f"/revocation/registry/{credential_def_id}/publish" + ) + + return revoc_publish_response["result"]["revoc_reg_id"] def get_agent_args(self): result = [ @@ -372,6 +415,23 @@ async def admin_POST( self.log(f"Error during POST {path}: {str(e)}") raise + async def admin_PATCH(self, path, data=None, text=False, params=None) -> ClientResponse: + try: + return await self.admin_request("PATCH", path, data, text, params) + except ClientError as e: + self.log(f"Error during PATCH {path}: {str(e)}") + raise + + async def admin_GET_FILE(self, path, params=None) -> bytes: + try: + params = {k: v for (k, v) in (params or {}).items() if v is not None} + resp = await self.client_session.request("GET", self.admin_url + path, params=params) + resp.raise_for_status() + return await resp.read() + except ClientError as e: + self.log(f"Error during GET FILE {path}: {str(e)}") + raise + async def detect_process(self): async def fetch_status(url: str, timeout: float): text = None