From 2fedc3bec4e7074ef1a7c2c6bac3864c85df15e5 Mon Sep 17 00:00:00 2001 From: jamshale Date: Mon, 8 Jan 2024 11:15:56 -0800 Subject: [PATCH] Add anoncred support Signed-off-by: jamshale --- aries-backchannels/acapy/acapy_backchannel.py | 117 ++++++++++-------- .../python/agent_backchannel.py | 18 ++- .../agent_backchannel_client.py | 26 ++-- aries-test-harness/agent_test_utils.py | 27 ++-- .../features/0434-out-of-band.feature | 6 +- .../features/0453-issue-credential-v2.feature | 2 +- .../features/0454-present-proof-v2.feature | 2 +- .../anoncreds_schema_driverslicense_v2.json | 16 +++ ...ta_anoncreds_schema_driverslicense_v2.json | 59 +++++++++ aries-test-harness/features/environment.py | 21 +++- .../features/steps/0036-issue-credential.py | 81 +++++++----- .../steps/0453-issue-credential-v2.py | 21 ++-- 12 files changed, 263 insertions(+), 133 deletions(-) create mode 100644 aries-test-harness/features/data/anoncreds_schema_driverslicense_v2.json create mode 100644 aries-test-harness/features/data/cred_data_anoncreds_schema_driverslicense_v2.json diff --git a/aries-backchannels/acapy/acapy_backchannel.py b/aries-backchannels/acapy/acapy_backchannel.py index 7c0c57fa..b1485714 100644 --- a/aries-backchannels/acapy/acapy_backchannel.py +++ b/aries-backchannels/acapy/acapy_backchannel.py @@ -6,40 +6,21 @@ import signal import subprocess import sys - from timeit import default_timer -from typing_extensions import Literal from typing import Any, Dict, List, Mapping, Optional, Tuple, Union -from aiohttp import ( - web, - ClientSession, - ClientRequest, - ClientError, - ClientTimeout, -) - -from python.agent_backchannel import ( - AgentBackchannel, - AgentPorts, - BackchannelCommand, - default_genesis_txns, - RUN_MODE, - START_TIMEOUT, -) -from python.utils import flatten, log_msg, output_reader, prompt_loop -from python.storage import ( - get_resource, - push_resource, - pop_resource, - pop_resource_latest, -) - from acapy.routes.agent_routes import routes as agent_routes -from acapy.routes.mediation_routes import ( - routes as mediation_routes, - get_mediation_record_by_connection_id, -) +from acapy.routes.mediation_routes import get_mediation_record_by_connection_id +from acapy.routes.mediation_routes import routes as mediation_routes +from aiohttp import (ClientError, ClientRequest, ClientSession, ClientTimeout, + web) +from python.agent_backchannel import (RUN_MODE, START_TIMEOUT, + AgentBackchannel, AgentPorts, + BackchannelCommand, default_genesis_txns) +from python.storage import (get_resource, pop_resource, pop_resource_latest, + push_resource) +from python.utils import flatten, log_msg, output_reader, prompt_loop +from typing_extensions import Literal # from helpers.jsonmapper.json_mapper import JsonMapper @@ -630,7 +611,7 @@ async def make_agent_POST_request( agent_operation += f"?mediation_id={mediation_id}" (resp_status, resp_text) = await self.admin_POST( - agent_operation, data=command.data + agent_operation, data=data ) if resp_status == 200: resp_text = self.agent_state_translation(command.topic, resp_text) @@ -651,10 +632,10 @@ async def make_agent_POST_request( if not await self.expected_agent_state( f"/connections/{connection_id}", "request", wait_time=60.0 ): - raise Exception(f"Expected state request but not received") + raise Exception("Expected state request but not received") agent_operation = f"/connections/{connection_id}/{operation}" - log_msg("POST Request: ", agent_operation, command.data) + log_msg("POST Request: ", agent_operation, data) if self.auto_accept_requests and operation == "accept-request": resp_status = 200 @@ -676,14 +657,22 @@ async def make_agent_POST_request( elif command.topic == "schema": # POST operation is to create a new schema - agent_operation = "/schemas" - log_msg(agent_operation, command.data) + if command.anoncreds: + schema_name = data['schema'].get("name") + schema_version = data['schema'].get("version") + schema_get_endpoint = '/anoncreds/schemas' + schema_post_endpoint = '/anoncreds/schema' + else: + schema_name = data.get("schema_name") + schema_version = data.get("schema_version") + schema_get_endpoint = '/schemas/created' + schema_post_endpoint = '/schemas' # Check if schema id already exists - schema_name = command.data.get("schema_name") - schema_version = command.data.get("schema_version") + log_msg(schema_post_endpoint, data) + (resp_status, resp_text) = await self.admin_GET( - "/schemas/created", + schema_get_endpoint, params={"schema_version": schema_version, "schema_name": schema_name}, ) resp_json = json.loads(resp_text) @@ -692,7 +681,7 @@ async def make_agent_POST_request( return (200, json.dumps({"schema_id": schema_id})) (resp_status, resp_text) = await self.admin_POST( - agent_operation, command.data + schema_post_endpoint, data ) log_msg(resp_status, resp_text) @@ -701,14 +690,23 @@ async def make_agent_POST_request( elif command.topic == "credential-definition": # POST operation is to create a new cred def + if command.anoncreds: + schema_id = data['credential_definition'].get("schemaId") + tag = data['credential_definition'].get("tag") + cred_defs_get_endpoint = '/anoncreds/credential-definitions' + cred_defs_post_endpoint = '/anoncreds/credential-definition' + else: + tag = data.get("tag") + schema_id = data.get("schema_id") + cred_defs_get_endpoint = '/credential-definitions/created' + cred_defs_post_endpoint = '/credential-definitions' + agent_operation = "/credential-definitions" - log_msg(agent_operation, command.data) + log_msg(cred_defs_post_endpoint, data) # Check if credential definition id already exists - schema_id = command.data.get("schema_id") - tag = command.data.get("tag") (resp_status, resp_text) = await self.admin_GET( - "/credential-definitions/created", + cred_defs_get_endpoint, params={ "schema_id": schema_id, }, @@ -725,7 +723,7 @@ async def make_agent_POST_request( ) (resp_status, resp_text) = await self.admin_POST( - agent_operation, command.data + cred_defs_post_endpoint, data ) log_msg(resp_status, resp_text) @@ -788,7 +786,7 @@ async def make_agent_POST_request( wait_time=60.0, ): raise Exception( - f"Expected state request-received but not received" + "Expected state request-received but not received" ) # Make Special provisions for revoke since it is passing multiple query params not just one id. @@ -928,7 +926,7 @@ async def make_agent_POST_request( wait_time=60.0, ): raise Exception( - f"Expected state presentation-received but not received" + "Expected state presentation-received but not received" ) else: @@ -1452,7 +1450,10 @@ async def make_agent_GET_request( elif command.topic == "schema": schema_id = record_id - agent_operation = f"/schemas/{schema_id}" + if command.anoncreds: + agent_operation = f"/anoncreds/schema/{schema_id}" + else: + agent_operation = f"/schemas/{schema_id}" (resp_status, resp_text) = await self.admin_GET(agent_operation) if resp_status != 200: @@ -1461,12 +1462,20 @@ async def make_agent_GET_request( resp_json = json.loads(resp_text) schema = resp_json["schema"] + # If anoncreds, add the id to the schema to use existing framework + if command.anoncreds: + schema["id"] = resp_json["schema_id"] + resp_text = json.dumps(schema) return (resp_status, resp_text) elif command.topic == "credential-definition": cred_def_id = record_id - agent_operation = f"/credential-definitions/{cred_def_id}" + + if command.anoncreds: + agent_operation = f"/anoncreds/credential-definition/{cred_def_id}" + else: + agent_operation = f"/credential-definitions/{cred_def_id}" (resp_status, resp_text) = await self.admin_GET(agent_operation) if resp_status != 200: @@ -1475,6 +1484,10 @@ async def make_agent_GET_request( resp_json = json.loads(resp_text) credential_definition = resp_json["credential_definition"] + # If anoncreds, add the id to the credential definition to use existing framework + if command.anoncreds: + credential_definition["id"] = resp_json["credential_definition_id"] + resp_text = json.dumps(credential_definition) return (resp_status, resp_text) @@ -1836,8 +1849,8 @@ async def start_process_with_extra_args( agent_args = self.get_process_args(bin_path) + args # start agent sub-process - self.log(f"Starting agent sub-process ...") - self.log(f"agent starting with params: ") + self.log("Starting agent sub-process ...") + self.log("agent starting with params: ") self.log(agent_args) self.log("and environment:") self.log(my_env) @@ -1863,8 +1876,8 @@ async def start_process( ) # start agent sub-process - self.log(f"Starting agent sub-process ...") - self.log(f"agent starting with params: ") + self.log("Starting agent sub-process ...") + self.log("agent starting with params: ") self.log(agent_args) loop = asyncio.get_event_loop() self.proc = await loop.run_in_executor( diff --git a/aries-backchannels/python/agent_backchannel.py b/aries-backchannels/python/agent_backchannel.py index 32672e85..2f64a3b5 100644 --- a/aries-backchannels/python/agent_backchannel.py +++ b/aries-backchannels/python/agent_backchannel.py @@ -1,16 +1,15 @@ import json import logging import os -import traceback import random - -from typing_extensions import TypedDict, Literal -from typing import Any, Optional, Tuple +import traceback from dataclasses import dataclass +from typing import Any, Optional, Tuple -from aiohttp import web, ClientSession -from aiohttp.typedefs import Handler import ptvsd +from aiohttp import ClientSession, web +from aiohttp.typedefs import Handler +from typing_extensions import Literal, TypedDict from .utils import log_msg @@ -59,6 +58,7 @@ class BackchannelCommand: operation: Optional[str] record_id: Optional[str] data: Optional[Any] + anoncreds: Optional[bool] = False async def default_genesis_txns(): @@ -225,6 +225,11 @@ async def parse_request(self, request: web.Request) -> BackchannelCommand: record_id = request.match_info.get("id", None) operation = request.match_info.get("operation", None) topic = request.match_info.get("topic", None) + anoncreds = request.query.get("anoncreds", False) + + if anoncreds == 'True': + anoncreds = True + method = request.method if not topic: @@ -248,6 +253,7 @@ async def parse_request(self, request: web.Request) -> BackchannelCommand: topic=topic, method=method, data=data, + anoncreds=anoncreds ) def not_found_response(self, data: Any): diff --git a/aries-test-harness/agent_backchannel_client.py b/aries-test-harness/agent_backchannel_client.py index e998a6f8..efc3d918 100644 --- a/aries-test-harness/agent_backchannel_client.py +++ b/aries-test-harness/agent_backchannel_client.py @@ -1,16 +1,10 @@ import asyncio -from aiohttp import ( - web, - ClientSession, - ClientRequest, - ClientResponse, - ClientError, - ClientTimeout, -) import json import os.path from time import sleep +from aiohttp import ClientSession + ###################################################################### # coroutine utilities ###################################################################### @@ -70,26 +64,30 @@ def sorted_payload(val, level=0): with open('/aries-test-harness/logs/request.log', 'a') as fout: print(f'Req: {method} {agent_url} {sorted_payload(payload)}', file=fout) print(f'Res: {resp_status} {sorted_payload(resp_text)}', file=fout) - print(f'-----', file=fout) + print('-----', file=fout) -def agent_backchannel_GET(url, topic, operation=None, id=None) -> (int, str): +def agent_backchannel_GET(url, topic, operation=None, id=None, anoncreds=False) -> (int, str): agent_url = url + topic + "/" + params = {} if operation: agent_url = agent_url + operation + "/" if id: agent_url = agent_url + id + if (anoncreds): + params["anoncreds"] = 'True' (resp_status, resp_text) = run_coroutine_with_kwargs( - make_agent_backchannel_request, "GET", agent_url + make_agent_backchannel_request, "GET", agent_url, params=params ) request_log("GET", agent_url, resp_status, resp_text) return (resp_status, resp_text) def agent_backchannel_POST( - url, topic, operation=None, id=None, data=None + url, topic, operation=None, id=None, data=None, anoncreds=False ) -> (int, str): agent_url = url + topic + "/" payload = {} + params = {} if data: payload["data"] = data if operation: @@ -99,8 +97,10 @@ def agent_backchannel_POST( payload["cred_ex_id"] = id else: payload["id"] = id + if anoncreds: + params["anoncreds"] = 'True' (resp_status, resp_text) = run_coroutine_with_kwargs( - make_agent_backchannel_request, "POST", agent_url, data=payload + make_agent_backchannel_request, "POST", agent_url, data=payload, params=params ) request_log("POST", agent_url, resp_status, resp_text, payload) return (resp_status, resp_text) diff --git a/aries-test-harness/agent_test_utils.py b/aries-test-harness/agent_test_utils.py index ce091dd6..f69e4515 100644 --- a/aries-test-harness/agent_test_utils.py +++ b/aries-test-harness/agent_test_utils.py @@ -1,5 +1,5 @@ -import time import datetime +import time from typing import Optional @@ -73,45 +73,44 @@ def format_cred_proposal_by_aip_version( return credential_proposal +def get_schema_name(context): + if context.anoncreds: + return context.schema["schema"]["name"] + else: + return context.schema["schema_name"] + def amend_filters_with_runtime_data(context, filters): + schema_name = get_schema_name(context) # This method will need comdification as new types of filters are used. Intially "indy" is used. if "indy" in filters: if ( "schema_issuer_did" in filters["indy"] and filters["indy"]["schema_issuer_did"] == "replace_me" ): - filters["indy"]["schema_issuer_did"] = context.issuer_did_dict[ - context.schema["schema_name"] - ] + filters["indy"]["schema_issuer_did"] = context.issuer_did_dict[schema_name] if ( "issuer_did" in filters["indy"] and filters["indy"]["issuer_did"] == "replace_me" ): - filters["indy"]["issuer_did"] = context.issuer_did_dict[ - context.schema["schema_name"] - ] + filters["indy"]["issuer_did"] = context.issuer_did_dict[schema_name] if ( "cred_def_id" in filters["indy"] and filters["indy"]["cred_def_id"] == "replace_me" ): - filters["indy"]["cred_def_id"] = context.issuer_credential_definition_dict[ - context.schema["schema_name"] - ]["id"] + filters["indy"]["cred_def_id"] = context.issuer_credential_definition_dict[schema_name]["id"] if ( "schema_id" in filters["indy"] and filters["indy"]["schema_id"] == "replace_me" ): - filters["indy"]["schema_id"] = context.issuer_schema_dict[ - context.schema["schema_name"] - ]["id"] + filters["indy"]["schema_id"] = context.issuer_schema_dict[schema_name]["id"] if "json-ld" in filters: json_ld = filters.get("json-ld") credential = json_ld.get("credential") options = json_ld.get("options") - issuer = context.issuer_did_dict[context.schema["schema_name"]] + issuer = context.issuer_did_dict[schema_name] if "issuer" in credential: # "issuer": "replace_me" diff --git a/aries-test-harness/features/0434-out-of-band.feature b/aries-test-harness/features/0434-out-of-band.feature index 79405518..35d1adae 100644 --- a/aries-test-harness/features/0434-out-of-band.feature +++ b/aries-test-harness/features/0434-out-of-band.feature @@ -21,7 +21,7 @@ Feature: RFC 0434 Intiating exchange using the Out of Band protocol Then "Bob" has the credential issued - @T002-RFC0434 @RFC0453 @critical @AcceptanceTest @Schema_DriversLicense_v2 + @T002-RFC0434 @RFC0453 @critical @AcceptanceTest @Schema_DriversLicense_v2 Scenario Outline: Issue a v2 credential using connectionless out of band invitation Given we have "2" agents | name | role | @@ -36,7 +36,7 @@ Feature: RFC 0434 Intiating exchange using the Out of Band protocol And "Bob" acknowledges the "indy" credential issue Then "Bob" has the "indy" credential issued - @CredFormat_Indy @RFC0592 + @CredFormat_Indy @RFC0592 @Anoncreds Examples: | credential_data | | Data_DL_MaxValues | @@ -74,7 +74,7 @@ Feature: RFC 0434 Intiating exchange using the Out of Band protocol And "Faber" acknowledges the proof with formats Then "Bob" has the proof with formats verified - @CredFormat_Indy @RFC0592 + @CredFormat_Indy @RFC0592 @Anoncreds Examples: | credential_data | request_for_proof | presentation | | Data_DL_MaxValues | proof_request_DL_address_v2 | presentation_DL_address_v2 | diff --git a/aries-test-harness/features/0453-issue-credential-v2.feature b/aries-test-harness/features/0453-issue-credential-v2.feature index 80515591..0a8a7fb4 100644 --- a/aries-test-harness/features/0453-issue-credential-v2.feature +++ b/aries-test-harness/features/0453-issue-credential-v2.feature @@ -1,7 +1,7 @@ @RFC0453 @AIP20 Feature: RFC 0453 Aries Agent Issue Credential v2 - @T001-RFC0453 @RFC0592 @critical @AcceptanceTest @CredFormat_Indy @Schema_DriversLicense_v2 + @T001-RFC0453 @RFC0592 @critical @AcceptanceTest @CredFormat_Indy @Schema_DriversLicense_v2 @Anoncreds Scenario Outline: Issue a Indy credential with the Holder beginning with a proposal Given "2" agents | name | role | diff --git a/aries-test-harness/features/0454-present-proof-v2.feature b/aries-test-harness/features/0454-present-proof-v2.feature index 0f27e6d4..6bb6ec9c 100644 --- a/aries-test-harness/features/0454-present-proof-v2.feature +++ b/aries-test-harness/features/0454-present-proof-v2.feature @@ -14,7 +14,7 @@ Feature: RFC 0454 Aries agent present proof v2 And "Faber" acknowledges the proof with formats Then "Bob" has the proof with formats verified - @CredFormat_Indy @RFC0592 @Schema_DriversLicense_v2 @CredProposalStart + @CredFormat_Indy @RFC0592 @Schema_DriversLicense_v2 @CredProposalStart @Anoncreds Examples: | issuer | credential_data | request_for_proof | presentation | | Acme | Data_DL_MaxValues | proof_request_DL_address_v2 | presentation_DL_address_v2 | diff --git a/aries-test-harness/features/data/anoncreds_schema_driverslicense_v2.json b/aries-test-harness/features/data/anoncreds_schema_driverslicense_v2.json new file mode 100644 index 00000000..f4483cfd --- /dev/null +++ b/aries-test-harness/features/data/anoncreds_schema_driverslicense_v2.json @@ -0,0 +1,16 @@ +{ + "schema":{ + "schema": { + "name":"Schema_DriversLicense_v2", + "version":"1.0.1", + "attrNames":[ + "address", + "DL_number", + "expiry", + "age" + ] + }, + "options":{} + }, + "cred_def_support_revocation":false +} \ No newline at end of file diff --git a/aries-test-harness/features/data/cred_data_anoncreds_schema_driverslicense_v2.json b/aries-test-harness/features/data/cred_data_anoncreds_schema_driverslicense_v2.json new file mode 100644 index 00000000..aafa9c47 --- /dev/null +++ b/aries-test-harness/features/data/cred_data_anoncreds_schema_driverslicense_v2.json @@ -0,0 +1,59 @@ +{ + "Data_DL_MaxValues":{ + "cred_name":"Data_DriversLicense_v2_MaxValues", + "schema_name":"Schema_DriversLicense_v2", + "schema_version":"1.0.1", + "attributes":[ + { + "name":"address", + "value":"947 this street, Kingston Ontario Canada, K9O 3R5" + }, + { + "name":"DL_number", + "value":"09385029529385" + }, + { + "name":"expiry", + "value":"10/12/2022" + }, + { + "name":"age", + "value":"30" + } + ], + "filters": { + "indy": { + "cred_def_id": "replace_me" + }, + "json-ld": { + "credential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/bbs/v1", + { + "dl": "http://example.com/drivers-license#", + "AATHDriversLicense": "dl:AATHDriversLicense", + "address": "dl:address", + "DL_number": "dl:DL_number", + "expiry": "dl:expiry", + "age": "dl:age" + } + ], + "type": ["VerifiableCredential", "AATHDriversLicense"], + "issuer": "replace_me", + "issuanceDate": "2010-01-01T19:73:24Z", + "credentialSubject": { + "address": "947 this street, Kingston Ontario Canada, K9O 3R5", + "DL_number": "09385029529385", + "expiry": "10/12/2022", + "age": "30" + } + }, + "options": { + "proofType": "replace_me" + } + + } + } + } +} \ No newline at end of file diff --git a/aries-test-harness/features/environment.py b/aries-test-harness/features/environment.py index e489c936..b5f1dac0 100644 --- a/aries-test-harness/features/environment.py +++ b/aries-test-harness/features/environment.py @@ -5,12 +5,14 @@ # https://behave.readthedocs.io/en/latest/tutorial.html#environmental-controls # # ----------------------------------------------------------- -import os import json +import os from collections import defaultdict -from behave.runner import Context -from behave.model import Scenario, Feature + from behave.contrib.scenario_autoretry import patch_scenario_with_autoretry +from behave.model import Feature, Scenario +from behave.runner import Context + def before_step(context: Context, step): context.step = step @@ -61,6 +63,8 @@ def before_scenario(context: Context, scenario: Scenario): context.did_method = tag.split("DidMethod_")[1] if "Schema_" in tag: # Get and assign the schema to the context + if context.anoncreds: + tag = tag.replace("Schema_", "Anoncreds_Schema_") try: schema_json_file = open("features/data/" + tag.lower() + ".json") schema_json = json.load(schema_json_file) @@ -442,6 +446,17 @@ def setup_scenario_context(context: Context, scenario: Scenario): # } context.mediator_dict = {} + def is_anoncreds(): + try: + return json.loads(os.environ["EXTRA_ARGS"])['wallet-type'] == "askar-anoncreds" + except Exception: + return False + + # Feature run with askar-anoncreds wallet + # + # context.anoncreds = True + context.anoncreds = is_anoncreds() + def before_feature(context, feature): # retry failed tests test_retry_attempts = None diff --git a/aries-test-harness/features/steps/0036-issue-credential.py b/aries-test-harness/features/steps/0036-issue-credential.py index e2622f90..d12a2bc8 100644 --- a/aries-test-harness/features/steps/0036-issue-credential.py +++ b/aries-test-harness/features/steps/0036-issue-credential.py @@ -1,13 +1,14 @@ -from behave import * import json -from agent_backchannel_client import ( - agent_backchannel_GET, - agent_backchannel_POST, - expected_agent_state, -) -from time import sleep import time -from random import * +from copy import deepcopy +from random import randint +from time import sleep + +from agent_backchannel_client import (agent_backchannel_GET, + agent_backchannel_POST, + expected_agent_state) +from agent_test_utils import get_schema_name +from behave import given, then, when # This step is defined in another feature file # Given "Acme" and "Bob" have an existing connection @@ -31,6 +32,15 @@ {"name": "attr_3", "value": "value_3"}, ] +CRED_DEF_TEMPLATE_ANONCREDS = { + "credential_definition": { + "schemaId": "placeholder", + "tag": str(randint(1, 10000)), + "issuerId": "placeholder", + }, + "options": {} +} + CRED_FORMAT_INDY = "indy" CRED_FORMAT_JSON_LD = "json-ld" @@ -52,7 +62,7 @@ def step_impl(context, issuer): context.schema = SCHEMA_TEMPLATE.copy() context.schema["schema_name"] = context.schema["schema_name"] + issuer - context.issuer_did_dict[context.schema["schema_name"]] = issuer_did["did"] + context.issuer_did_dict[get_schema_name(context)] = issuer_did["did"] @given('"{issuer}" is ready to issue a credential') @@ -76,18 +86,21 @@ def step_impl(context, issuer): # check for a schema template already loaded in the context. If it is, it was loaded from an external Schema, so use it. if context.schema: schema = context.schema + if context.anoncreds: + schema["schema"]["issuerId"] = context.issuer_did_dict[get_schema_name(context)] else: schema = SCHEMA_TEMPLATE.copy() schema["schema_name"] = schema["schema_name"] + issuer (resp_status, resp_text) = agent_backchannel_POST( - issuer_url + "/agent/command/", "schema", data=schema + issuer_url + "/agent/command/", "schema", data=schema, anoncreds=context.anoncreds ) assert resp_status == 200, f"resp_status {resp_status} is not 200; {resp_text}" resp_json = json.loads(resp_text) - context.issuer_schema_id_dict[context.schema["schema_name"]] = resp_json[ + + context.issuer_schema_id_dict[get_schema_name(context)] = resp_json[ "schema_id" ] @@ -95,15 +108,25 @@ def step_impl(context, issuer): @when('"{issuer}" creates a new credential definition') def step_impl(context, issuer): issuer_url = context.config.userdata.get(issuer) + schema_name = get_schema_name(context) - cred_def = CRED_DEF_TEMPLATE.copy() - cred_def["schema_id"] = context.issuer_schema_id_dict[context.schema["schema_name"]] + if context.anoncreds: + cred_def = deepcopy(CRED_DEF_TEMPLATE_ANONCREDS) + cred_def["credential_definition"]["schemaId"] = context.issuer_schema_id_dict[schema_name] + cred_def["credential_definition"]["issuerId"] = context.issuer_did_dict[schema_name] + cred_def["credential_definition"]["tag"] = str(randint(1, 10000)) + else: + cred_def = CRED_DEF_TEMPLATE.copy() + cred_def["schema_id"] = context.issuer_schema_id_dict[schema_name] if context.support_revocation: - cred_def["support_revocation"] = context.support_revocation + if context.anoncreds: + cred_def["options"]["support_revocation"] = context.support_revocation + else: + cred_def["support_revocation"] = context.support_revocation (resp_status, resp_text) = agent_backchannel_POST( - issuer_url + "/agent/command/", "credential-definition", data=cred_def + issuer_url + "/agent/command/", "credential-definition", data=cred_def, anoncreds=context.anoncreds ) assert resp_status == 200, f"resp_status {resp_status} is not 200; {resp_text}" @@ -113,43 +136,44 @@ def step_impl(context, issuer): # context.cred_rev_creation_time = resp_json["created"] context.cred_rev_creation_time = time.time() - context.credential_definition_id_dict[context.schema["schema_name"]] = resp_json[ + context.credential_definition_id_dict[get_schema_name(context)] = resp_json[ "credential_definition_id" ] @then('"{issuer}" has an existing schema') def step_impl(context, issuer): + schema_name = get_schema_name(context) issuer_url = context.config.userdata.get(issuer) - issuer_schema_id = context.issuer_schema_id_dict[context.schema["schema_name"]] + issuer_schema_id = context.issuer_schema_id_dict[schema_name] (resp_status, resp_text) = agent_backchannel_GET( - issuer_url + "/agent/command/", "schema", id=issuer_schema_id + issuer_url + "/agent/command/", "schema", id=issuer_schema_id, anoncreds=context.anoncreds ) assert resp_status == 200, f"resp_status {resp_status} is not 200; {resp_text}" resp_json = json.loads(resp_text) - context.issuer_schema_dict[context.schema["schema_name"]] = resp_json + context.issuer_schema_dict[schema_name] = resp_json @then('"{issuer}" has an existing credential definition') def step_impl(context, issuer): + schema_name = get_schema_name(context) issuer_url = context.config.userdata.get(issuer) - issuer_credential_definition_id = context.credential_definition_id_dict[ - context.schema["schema_name"] - ] + issuer_credential_definition_id = context.credential_definition_id_dict[schema_name] (resp_status, resp_text) = agent_backchannel_GET( issuer_url + "/agent/command/", "credential-definition", id=issuer_credential_definition_id, + anoncreds=context.anoncreds, ) assert resp_status == 200, f"resp_status {resp_status} is not 200; {resp_text}" resp_json = json.loads(resp_text) - context.issuer_credential_definition_dict[context.schema["schema_name"]] = resp_json + context.issuer_credential_definition_dict[schema_name] = resp_json @given('"{issuer}" has an existing schema and credential definition') @@ -204,15 +228,14 @@ def step_impl(context, issuer): @when('"{holder}" proposes a credential to "{issuer}"') def step_impl(context, holder, issuer): holder_url = context.config.userdata.get(holder) + schema_name = get_schema_name(context) # check for a schema template already loaded in the context. If it is, it was loaded from an external Schema, so use it. cred_data = context.credential_data or CREDENTIAL_ATTR_TEMPLATE.copy() - issuer_did = context.issuer_did_dict[context.schema["schema_name"]] - issuer_schema = context.issuer_schema_dict[context.schema["schema_name"]] - issuer_cred_def = context.issuer_credential_definition_dict[ - context.schema["schema_name"] - ] + issuer_did = context.issuer_did_dict[schema_name] + issuer_schema = context.issuer_schema_dict[schema_name] + issuer_cred_def = context.issuer_credential_definition_dict[schema_name] credential_offer = { "schema_issuer_did": issuer_did, "issuer_did": issuer_did, @@ -306,7 +329,7 @@ def step_impl(context, issuer): cred_data = context.credential_data or CREDENTIAL_ATTR_TEMPLATE.copy() - cred_def_id = context.issuer_credential_definition_dict[context.schema["schema_name"]]["id"] + cred_def_id = context.issuer_credential_definition_dict[get_schema_name(context)]["id"] credential_offer = { "cred_def_id": cred_def_id, diff --git a/aries-test-harness/features/steps/0453-issue-credential-v2.py b/aries-test-harness/features/steps/0453-issue-credential-v2.py index c478ddc4..fb61e84e 100644 --- a/aries-test-harness/features/steps/0453-issue-credential-v2.py +++ b/aries-test-harness/features/steps/0453-issue-credential-v2.py @@ -1,9 +1,10 @@ -from behave import * import json -from agent_backchannel_client import agent_backchannel_GET, agent_backchannel_POST -from agent_test_utils import format_cred_proposal_by_aip_version from time import sleep +from agent_backchannel_client import agent_backchannel_GET, agent_backchannel_POST +from agent_test_utils import format_cred_proposal_by_aip_version, get_schema_name +from behave import given, then, when + CRED_FORMAT_INDY = "indy" CRED_FORMAT_JSON_LD = "json-ld" @@ -35,7 +36,6 @@ def setup_schemas_for_issuance(context, credential_data): ] context.filters = context.filters_dict[schema] - @given('"{issuer}" is ready to issue a "{cred_format}" credential') def step_impl(context, issuer: str, cred_format: str = CRED_FORMAT_INDY): if cred_format == CRED_FORMAT_INDY: @@ -64,7 +64,7 @@ def step_impl(context, issuer: str, cred_format: str = CRED_FORMAT_INDY): resp_json = json.loads(resp_text) # TODO: it would be nice to not depend on the schema name for the issuer did dict - context.issuer_did_dict[context.schema["schema_name"]] = resp_json["did"] + context.issuer_did_dict[get_schema_name(context)] = resp_json["did"] else: raise Exception(f"Unknown credential format {cred_format}") @@ -250,7 +250,7 @@ def step_impl(context, holder, cred_format): # FIXME: the return value of this is very ACA-Py specific, should just be credential_id credential_id = resp_json[cred_format]["credential_id"] - context.credential_id_dict[context.schema["schema_name"]].append(credential_id) + context.credential_id_dict[get_schema_name(context)].append(credential_id) # Verify issuer status # TODO This is returning none instead of Done. Should this be the case. Needs investigation. @@ -274,11 +274,12 @@ def step_impl(context, holder, cred_format): def step_impl(context, holder, cred_format): holder_url = context.config.userdata.get(holder) + cred_id = context.credential_id_dict[get_schema_name(context)][-1] # get the credential from the holders wallet (resp_status, resp_text) = agent_backchannel_GET( holder_url + "/agent/command/", "credential", - id=context.credential_id_dict[context.schema["schema_name"]][-1], + id=cred_id, ) assert resp_status == 200, f"resp_status {resp_status} is not 200; {resp_text}" resp_json = json.loads(resp_text) @@ -287,14 +288,12 @@ def step_impl(context, holder, cred_format): # assert resp_json["schema_id"] == context.issuer_schema_id_dict[context.schema["schema_name"]] # assert resp_json["cred_def_id"] == context.credential_definition_id_dict[context.schema["schema_name"]] assert ( - resp_json["referent"] - == context.credential_id_dict[context.schema["schema_name"]][-1] + resp_json["referent"] == cred_id ) elif cred_format == CRED_FORMAT_JSON_LD: # TODO: do not use schema name for credential_id_dict assert ( - resp_json["credential_id"] - == context.credential_id_dict[context.schema["schema_name"]][-1] + resp_json["credential_id"] == cred_id )