From eebdd01e14c475f83de7d60dff9c94b63700665d Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 4 Dec 2023 10:55:23 -0800 Subject: [PATCH 1/5] Update integration tests for anoncreds-rs Signed-off-by: Ian Costanzo --- AnonCredsWalletType.md | 10 +- aries_cloudagent/config/default_context.py | 8 +- demo/bdd_support/agent_backchannel_client.py | 10 ++ demo/features/revocation-api.feature | 131 ++++++++++++++++ demo/features/steps/0453-issue-credential.py | 5 +- demo/features/steps/0454-present-proof.py | 14 ++ demo/features/steps/0586-sign-transaction.py | 150 ++++++++++++++++++- demo/features/steps/revocation-api.py | 75 ++++++++++ demo/runners/agent_container.py | 9 ++ demo/runners/support/agent.py | 24 +++ 10 files changed, 424 insertions(+), 12 deletions(-) create mode 100644 demo/features/revocation-api.feature create mode 100644 demo/features/steps/revocation-api.py diff --git a/AnonCredsWalletType.md b/AnonCredsWalletType.md index 0b508b1d7c..85ee371e9d 100644 --- a/AnonCredsWalletType.md +++ b/AnonCredsWalletType.md @@ -80,14 +80,14 @@ The Tails file changes are minimal -- nothing about the file itself changed. Wh ## Outstanding work +* revocation notifications (not sure if they're included in `anoncreds-rs` updates, haven't tested them ...) +* revocation support - complete the revocation implementation (support for unhappy path scenarios) +* endpoints - don't load the schema/cred-def endpoints when wallet type is anoncreds (will require some BDD updates) +* testing - various scenarios like mediation, multitenancy etc. + - unit tests (in the new anoncreds package) (see https://github.com/hyperledger/aries-cloudagent-python/pull/2596/commits/229ffbba209aff0ea7def5bad6556d93057f3c2a) - unit tests (review and possibly update unit tests for the credential and presentation integration) -- revocation support - migrate code from `anoncreds-rs` branch (in progress) -- revocation notifications (not sure if they're included in `anoncreds-rs` updates, haven'e tested them ...) -- revocation support - complete the revocation implementation (support for unhappy path scenarios) - endorsement (not implemented with new anoncreds code) -- endpoints - don't load the schema/cred-def endpoints when wallet type is anoncreds (will require some BDD updates) -- testing - various scenarios like mediation, multitenancy etc. - wallet upgrade (askar to askar-anoncreds) - update V1.0 versions of the Credential and Presentation endpoints to use anoncreds - any other anoncreds issues - https://github.com/hyperledger/aries-cloudagent-python/issues?q=is%3Aopen+is%3Aissue+label%3AAnonCreds diff --git a/aries_cloudagent/config/default_context.py b/aries_cloudagent/config/default_context.py index ebc25bd42c..283679c97d 100644 --- a/aries_cloudagent/config/default_context.py +++ b/aries_cloudagent/config/default_context.py @@ -138,10 +138,6 @@ async def load_plugins(self, context: InjectionContext): # Currently providing admin routes only plugin_registry.register_plugin("aries_cloudagent.holder") plugin_registry.register_plugin("aries_cloudagent.ledger") - plugin_registry.register_plugin( - "aries_cloudagent.messaging.credential_definitions" - ) - plugin_registry.register_plugin("aries_cloudagent.messaging.schemas") plugin_registry.register_plugin("aries_cloudagent.messaging.jsonld") plugin_registry.register_plugin("aries_cloudagent.resolver") plugin_registry.register_plugin("aries_cloudagent.settings") @@ -159,6 +155,10 @@ async def load_plugins(self, context: InjectionContext): ) plugin_registry.register_plugin("aries_cloudagent.revocation_anoncreds") else: + plugin_registry.register_plugin( + "aries_cloudagent.messaging.credential_definitions" + ) + plugin_registry.register_plugin("aries_cloudagent.messaging.schemas") plugin_registry.register_plugin("aries_cloudagent.revocation") if context.settings.get("multitenant.admin_enabled"): diff --git a/demo/bdd_support/agent_backchannel_client.py b/demo/bdd_support/agent_backchannel_client.py index 9b0c9c0e6c..a3f99524d2 100644 --- a/demo/bdd_support/agent_backchannel_client.py +++ b/demo/bdd_support/agent_backchannel_client.py @@ -103,6 +103,16 @@ def aries_container_create_schema_cred_def( ) +def aries_container_check_exists_cred_def( + the_container: AgentContainer, + cred_def_id: str, +): + return run_coroutine( + the_container.check_exists_cred_def, + cred_def_id, + ) + + def aries_container_issue_credential( the_container: AgentContainer, cred_def_id: str, diff --git a/demo/features/revocation-api.feature b/demo/features/revocation-api.feature new file mode 100644 index 0000000000..b428c623d0 --- /dev/null +++ b/demo/features/revocation-api.feature @@ -0,0 +1,131 @@ +Feature: ACA-Py Revocation API + + @GHA + Scenario Outline: Using revocation api, issue and revoke credentials + Given we have "3" agents + | name | role | capabilities | + | Acme | issuer | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" revokes the credential + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @GHA + Scenario Outline: Using revocation api, issue, revoke credentials and publish + Given we have "3" agents + | name | role | capabilities | + | Acme | issuer | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" has written the credential definition for to the ledger + And "" has written the revocation registry definition to the ledger ignore count + And "" has written the revocation registry entry transaction to the ledger + And "" revokes the credential without publishing the entry + And "" authors a revocation registry entry publishing transaction + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + Then "Bob" can verify the credential from "" was revoked + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @GHA-Anoncreds-break + Scenario Outline: Without endorser: issue, revoke credentials, manually create revocation registries + Given we have "3" agents + | name | role | capabilities | + | Acme | issuer | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And Without endorser, "" authors a schema transaction with + And "" has written the schema to the ledger + And Without endorser, "" authors a credential definition transaction with ' + And "" has written the credential definition for to the ledger + And Without endorser, "" authors a revocation registry definition transaction for the credential definition matching + And Without endorser, "" has written the revocation registry definition to the ledger + And "" has activated the tails file, and uploaded it to the tails server + And Without endorser, "" authors a revocation registry entry transaction for the credential definition matching + And "" has written the revocation registry entry transaction to the ledger + And "" offers a credential with data + Then "Bob" has the credential issued + And "" revokes the credential without publishing the entry + And "" authors a revocation registry entry publishing transaction + And "Faber" and "Bob" have an existing connection + When "Faber" sends a request for proof presentation to "Bob" + Then "Faber" has the proof verification fail + Then "Bob" can verify the credential from "" was revoked + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Acme | --revocation --public-did --did-exchange | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @GHA + Scenario Outline: Using revocation api, rotate revocation + Given we have "3" agents + | name | role | capabilities | + | Acme | issuer | | + | Faber | verifier | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And "" lists revocation registries + And "" rotates revocation registries + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + + @GHA + Scenario Outline: Using revocation api, fill registry (need to run with "TAILS_FILE_COUNT": "4" env var) + Given we have "2" agents + | name | role | capabilities | + | Acme | issuer | | + | Bob | prover | | + And "" and "Bob" have an existing connection + And "Bob" has an issued credential from "" + And wait 5 seconds + And "" lists revocation registries 1 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 2 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 3 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 4 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 5 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 6 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + And "" lists revocation registries 7 + When "" offers a credential with data + Then "Bob" has the credential issued + And "" lists revocation registries 8 + When "" offers a credential with data + Then "Bob" has the credential issued + And wait 5 seconds + + Examples: + | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | + | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 41d5b65d9f..4d3a5bb324 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -5,6 +5,7 @@ from bdd_support.agent_backchannel_client import ( aries_container_create_schema_cred_def, + aries_container_check_exists_cred_def, aries_container_issue_credential, aries_container_receive_credential, read_schema_data, @@ -46,8 +47,8 @@ def step_impl(context, issuer, schema_name): # confirm the cred def was actually created # TODO for anoncreds, this should call the anoncreds/cred-def endpoint async_sleep(2.0) - cred_def_saved = agent_container_GET( - agent["agent"], "/credential-definitions/" + cred_def_id + cred_def_saved = aries_container_check_exists_cred_def( + agent["agent"], cred_def_id ) assert cred_def_saved diff --git a/demo/features/steps/0454-present-proof.py b/demo/features/steps/0454-present-proof.py index 8569f77821..030dd42f44 100644 --- a/demo/features/steps/0454-present-proof.py +++ b/demo/features/steps/0454-present-proof.py @@ -36,6 +36,20 @@ def step_impl(context, verifier, request_for_proof, prover): proof_request_info = read_proof_req_data(request_for_proof) + # replace any restrictions that need the cred def id... + cred_def_id = context.cred_def_id + cred_def_restrictions = [{"cred_def_id": cred_def_id}] + + if "cred_def_restriction" in proof_request_info["requested_attributes"]: + proof_request_info["requested_attributes"]["cred_def_restriction"][ + "restrictions" + ] = cred_def_restrictions + + if "cred_def_predicate" in proof_request_info["requested_predicates"]: + proof_request_info["requested_predicates"]["cred_def_predicate"][ + "restrictions" + ] = cred_def_restrictions + proof_exchange = aries_container_request_proof(agent["agent"], proof_request_info) context.proof_request = proof_request_info diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 29dc2b29a8..5a809eb3bb 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -179,6 +179,7 @@ def step_impl(context, agent_name): assert written_txn["state"] == "transaction_acked" +@given('"{agent_name}" has written the schema {schema_name} to the ledger') @when('"{agent_name}" has written the schema {schema_name} to the ledger') @then('"{agent_name}" has written the schema {schema_name} to the ledger') def step_impl(context, agent_name, schema_name): @@ -236,6 +237,9 @@ def step_impl(context, agent_name, schema_name): context.txn_ids["AUTHOR"] = created_txn["txn"]["transaction_id"] +@given( + '"{agent_name}" has written the credential definition for {schema_name} to the ledger' +) @when( '"{agent_name}" has written the credential definition for {schema_name} to the ledger' ) @@ -314,6 +318,7 @@ def step_impl(context, agent_name, schema_name): context.txn_ids["AUTHOR"] = created_txn["txn"]["transaction_id"] +@given('"{agent_name}" has written the revocation registry definition to the ledger') @when('"{agent_name}" has written the revocation registry definition to the ledger') @then('"{agent_name}" has written the revocation registry definition to the ledger') def step_impl(context, agent_name): @@ -364,6 +369,9 @@ def step_impl(context, agent_name): ) +@given( + '"{agent_name}" has written the revocation registry entry transaction to the ledger' +) @when( '"{agent_name}" has written the revocation registry entry transaction to the ledger' ) @@ -382,7 +390,7 @@ def step_impl(context, agent_name): f"/revocation/registry/{context.rev_reg_id}", ) state = reg_info["result"]["state"] - if state == "active": + if state in ["active", "finished"]: return i = i - 1 @@ -438,6 +446,7 @@ def step_impl(context, holder, schema_name, credential_data, issuer): ) +@given('"{agent_name}" revokes the credential without publishing the entry') @when('"{agent_name}" revokes the credential without publishing the entry') @then('"{agent_name}" revokes the credential without publishing the entry') def step_impl(context, agent_name): @@ -502,6 +511,7 @@ def step_impl(context, agent_name): async_sleep(3.0) +@given('"{agent_name}" authors a revocation registry entry publishing transaction') @when('"{agent_name}" authors a revocation registry entry publishing transaction') @then('"{agent_name}" authors a revocation registry entry publishing transaction') def step_impl(context, agent_name): @@ -585,3 +595,141 @@ def step_impl(context, holder_name, issuer_name): counter = counter + 1 async_sleep(1.0) assert revoc_status_bool is True + + +@given( + 'Without endorser, "{agent_name}" authors a schema transaction with {schema_name}' +) +def step_impl(context, agent_name, schema_name): + agent = context.active_agents[agent_name] + + schema_info = read_schema_data(schema_name) + connection_id = agent["agent"].agent.connection_id + + created_txn = agent_container_POST( + agent["agent"], + "/schemas", + data=schema_info["schema"], + params={"conn_id": connection_id, "create_transaction_for_endorser": "false"}, + ) + + # assert goodness + assert created_txn["schema_id"] + context.schema_id = created_txn["schema_id"] + + +@given( + 'Without endorser, "{agent_name}" authors a credential definition transaction with {schema_name}' +) +def step_impl(context, agent_name, schema_name): + agent = context.active_agents[agent_name] + + connection_id = agent["agent"].agent.connection_id + + # TODO for now assume there is a single schema; should find the schema based on the supplied name + schemas = agent_container_GET(agent["agent"], "/schemas/created") + assert len(schemas["schema_ids"]) == 1 + + schema_id = schemas["schema_ids"][0] + created_txn = agent_container_POST( + agent["agent"], + "/credential-definitions", + data={ + "schema_id": schema_id, + "tag": "test_cred_def_with_endorsement", + "support_revocation": True, + "revocation_registry_size": 1000, + }, + params={"conn_id": connection_id, "create_transaction_for_endorser": "false"}, + ) + + # assert goodness + assert created_txn["credential_definition_id"] + context.cred_def_id = created_txn["credential_definition_id"] + + +@given( + 'Without endorser, "{agent_name}" authors a revocation registry definition transaction for the credential definition matching {schema_name}' +) +def step_impl(context, agent_name, schema_name): + agent = context.active_agents[agent_name] + + connection_id = agent["agent"].agent.connection_id + + # generate revocation registry transaction + rev_reg = agent_container_POST( + agent["agent"], + "/revocation/create-registry", + data={"credential_definition_id": context.cred_def_id, "max_cred_num": 1000}, + params={}, + ) + rev_reg_id = rev_reg["result"]["revoc_reg_id"] + assert rev_reg_id is not None + + # update revocation registry + agent_container_PATCH( + agent["agent"], + f"/revocation/registry/{rev_reg_id}", + data={ + "tails_public_uri": f"http://host.docker.internal:6543/revocation/registry/{rev_reg_id}/tails-file" + }, + params={}, + ) + + # create rev_reg def + created_txn = agent_container_POST( + agent["agent"], + f"/revocation/registry/{rev_reg_id}/definition", + data={}, + params={ + "conn_id": connection_id, + "create_transaction_for_endorser": "false", + }, + ) + assert created_txn + context.rev_reg_id = rev_reg_id + + +@given( + 'Without endorser, "{agent_name}" has written the revocation registry definition to the ledger' +) +def step_impl(context, agent_name): + agent = context.active_agents[agent_name] + + rev_regs = {"rev_reg_ids": []} + i = 5 + while 0 == len(rev_regs["rev_reg_ids"]) and i > 0: + async_sleep(1.0) + rev_regs = agent_container_GET( + agent["agent"], + "/revocation/registries/created", + params={ + "cred_def_id": context.cred_def_id, + }, + ) + i = i - 1 + + assert context.rev_reg_id in rev_regs["rev_reg_ids"] + + +@given( + 'Without endorser, "{agent_name}" authors a revocation registry entry transaction for the credential definition matching {schema_name}' +) +def step_impl(context, agent_name, schema_name): + agent = context.active_agents[agent_name] + + connection_id = agent["agent"].agent.connection_id + + # generate revocation registry entry transaction + # create rev_reg transaction + created_txn = agent_container_POST( + agent["agent"], + f"/revocation/registry/{context.rev_reg_id}/entry", + data={}, + params={ + "conn_id": connection_id, + "create_transaction_for_endorser": "false", + }, + ) + assert created_txn + diff --git a/demo/features/steps/revocation-api.py b/demo/features/steps/revocation-api.py new file mode 100644 index 0000000000..72fda7dee0 --- /dev/null +++ b/demo/features/steps/revocation-api.py @@ -0,0 +1,75 @@ +from behave import given, when, then +import json +import os + +from bdd_support.agent_backchannel_client import ( + agent_container_GET, + agent_container_POST, + async_sleep, +) +from runners.agent_container import AgentContainer + + +BDD_EXTRA_AGENT_ARGS = os.getenv("BDD_EXTRA_AGENT_ARGS") + +# And "" lists revocation registries +# And "" rotates revocation registries + +@given("wait {count} seconds") +@then("wait {count} seconds") +def step_impl(context, count=None): + if count: + print(f"sleeping for {count} seconds..") + async_sleep(int(count)) + +@given('"{issuer}" lists revocation registries {count}') +@then('"{issuer}" lists revocation registries {count}') +def step_impl(context, issuer, count=None): + agent = context.active_agents[issuer] + async_sleep(5.0) + created_response = agent_container_GET( + agent["agent"], f"/revocation/registries/created" + ) + full_response = agent_container_GET( + agent["agent"], f"/revocation/registries/created", params={"state": "full"} + ) + decommissioned_response = agent_container_GET( + agent["agent"], f"/revocation/registries/created", params={"state": "decommissioned"} + ) + finished_response = agent_container_GET( + agent["agent"], f"/revocation/registries/created", params={"state": "finished"} + ) + async_sleep(4.0) + if count: + print(f"\nlists revocation registries ({count} creds) = = = = = = = = = = = = = =") + else: + print("\nlists revocation registries = = = = = = = = = = = = = = = = = = = = = =") + print("\ncreated_response: ", len(created_response["rev_reg_ids"])) + print("full_response: ", len(full_response["rev_reg_ids"])) + print("decommissioned_response:", len(decommissioned_response["rev_reg_ids"])) + print("finished_response: ", len(finished_response["rev_reg_ids"])) + async_sleep(1.0) + +@given('"{issuer}" rotates revocation registries') +@then('"{issuer}" rotates revocation registries') +def step_impl(context, issuer): + agent = context.active_agents[issuer] + cred_def_id = context.cred_def_id + original_active_response = agent_container_GET( + agent["agent"], f"/revocation/active-registry/{cred_def_id}" + ) + print("original_active_response:", json.dumps(original_active_response)) + + rotate_response = agent_container_POST( + agent["agent"], + f"/revocation/active-registry/{cred_def_id}/rotate", + data={}, + ) + print("rotate_response:", json.dumps(rotate_response)) + + async_sleep(10.0) + + active_response = agent_container_GET( + agent["agent"], f"/revocation/active-registry/{cred_def_id}" + ) + print("active_response:", json.dumps(active_response)) diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index 2916fffb27..fa7e0f003c 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -919,6 +919,15 @@ async def create_schema_and_cred_def( else: raise Exception("Invalid credential type:" + self.cred_type) + async def check_exists_cred_def( + self, + cred_def_id: str, + ): + return await self.agent.check_exists_cred_def( + cred_def_id, + wallet_type=self.agent.wallet_type, + ) + async def issue_credential( self, cred_def_id: str, diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 0983cc711f..9d3ace0274 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -291,6 +291,30 @@ async def register_schema_and_creddef( else: raise Exception("Invalid wallet_type: " + str(wallet_type)) + async def check_exists_cred_def( + self, + cred_def_id: str, + wallet_type=WALLET_TYPE_INDY, + ): + if wallet_type in [WALLET_TYPE_INDY, WALLET_TYPE_ASKAR]: + cred_def_saved = await self.admin_GET( + "/credential-definitions/" + cred_def_id + ) + if cred_def_saved: + return True + else: + return False + elif wallet_type == WALLET_TYPE_ANONCREDS: + cred_def_saved = await self.admin_GET( + "/anoncreds/credential-definition/" + cred_def_id + ) + if cred_def_saved: + return True + else: + return False + else: + raise Exception("Invalid wallet_type: " + str(wallet_type)) + async def register_schema_and_creddef_indy( self, schema_name, From bfa0a5ec91ad18f2e9cddc3bdf876908dc271cc0 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 4 Dec 2023 12:03:44 -0800 Subject: [PATCH 2/5] Tweak revocation integration tests Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 3 +-- demo/features/revocation-api.feature | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 22a3b1e62f..94f50dff56 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1684,8 +1684,7 @@ def get_settings(self, args: Namespace) -> dict: if args.recreate_wallet: settings["wallet.recreate"] = True # check required settings for 'indy' wallets - # TODO should this also include "askar*" wallet types? - if settings["wallet.type"] == "indy": + if settings["wallet.type"] in ["indy","askar","askar-anoncreds"]: # requires name, key if not args.wallet_name or not args.wallet_key: raise ArgsParseError( diff --git a/demo/features/revocation-api.feature b/demo/features/revocation-api.feature index b428c623d0..6c8c22a38c 100644 --- a/demo/features/revocation-api.feature +++ b/demo/features/revocation-api.feature @@ -1,6 +1,6 @@ Feature: ACA-Py Revocation API - @GHA + @Revoc-api @GHA Scenario Outline: Using revocation api, issue and revoke credentials Given we have "3" agents | name | role | capabilities | @@ -18,7 +18,7 @@ Feature: ACA-Py Revocation API | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @GHA + @Revoc-api @GHA Scenario Outline: Using revocation api, issue, revoke credentials and publish Given we have "3" agents | name | role | capabilities | @@ -28,7 +28,7 @@ Feature: ACA-Py Revocation API And "" and "Bob" have an existing connection And "Bob" has an issued credential from "" And "" has written the credential definition for to the ledger - And "" has written the revocation registry definition to the ledger ignore count + And "" has written the revocation registry definition to the ledger And "" has written the revocation registry entry transaction to the ledger And "" revokes the credential without publishing the entry And "" authors a revocation registry entry publishing transaction @@ -40,7 +40,7 @@ Feature: ACA-Py Revocation API | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @GHA-Anoncreds-break + @Revoc-api.x @GHA-Anoncreds-break Scenario Outline: Without endorser: issue, revoke credentials, manually create revocation registries Given we have "3" agents | name | role | capabilities | @@ -69,7 +69,7 @@ Feature: ACA-Py Revocation API | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Acme | --revocation --public-did --did-exchange | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @GHA + @Revoc-api @GHA Scenario Outline: Using revocation api, rotate revocation Given we have "3" agents | name | role | capabilities | @@ -78,14 +78,14 @@ Feature: ACA-Py Revocation API | Bob | prover | | And "" and "Bob" have an existing connection And "Bob" has an issued credential from "" - And "" lists revocation registries + And "" lists revocation registries 1 And "" rotates revocation registries Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @GHA + @Revoc-api @GHA Scenario Outline: Using revocation api, fill registry (need to run with "TAILS_FILE_COUNT": "4" env var) Given we have "2" agents | name | role | capabilities | From dce64fd055f5abbf1df9d93a775ff9ba7e647781 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Mon, 4 Dec 2023 13:13:27 -0800 Subject: [PATCH 3/5] Formatting fixes Signed-off-by: Ian Costanzo --- aries_cloudagent/config/argparse.py | 2 +- demo/features/steps/0453-issue-credential.py | 4 +--- demo/features/steps/0586-sign-transaction.py | 1 - demo/features/steps/revocation-api.py | 23 ++++++++++++++------ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 94f50dff56..016dff65a7 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -1684,7 +1684,7 @@ def get_settings(self, args: Namespace) -> dict: if args.recreate_wallet: settings["wallet.recreate"] = True # check required settings for 'indy' wallets - if settings["wallet.type"] in ["indy","askar","askar-anoncreds"]: + if settings["wallet.type"] in ["indy", "askar", "askar-anoncreds"]: # requires name, key if not args.wallet_name or not args.wallet_key: raise ArgsParseError( diff --git a/demo/features/steps/0453-issue-credential.py b/demo/features/steps/0453-issue-credential.py index 4d3a5bb324..5ccee12d5b 100644 --- a/demo/features/steps/0453-issue-credential.py +++ b/demo/features/steps/0453-issue-credential.py @@ -47,9 +47,7 @@ def step_impl(context, issuer, schema_name): # confirm the cred def was actually created # TODO for anoncreds, this should call the anoncreds/cred-def endpoint async_sleep(2.0) - cred_def_saved = aries_container_check_exists_cred_def( - agent["agent"], cred_def_id - ) + cred_def_saved = aries_container_check_exists_cred_def(agent["agent"], cred_def_id) assert cred_def_saved context.schema_name = schema_name diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 5a809eb3bb..305217a96e 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -732,4 +732,3 @@ def step_impl(context, agent_name, schema_name): }, ) assert created_txn - diff --git a/demo/features/steps/revocation-api.py b/demo/features/steps/revocation-api.py index 72fda7dee0..d7cfc271a8 100644 --- a/demo/features/steps/revocation-api.py +++ b/demo/features/steps/revocation-api.py @@ -15,6 +15,7 @@ # And "" lists revocation registries # And "" rotates revocation registries + @given("wait {count} seconds") @then("wait {count} seconds") def step_impl(context, count=None): @@ -22,6 +23,7 @@ def step_impl(context, count=None): print(f"sleeping for {count} seconds..") async_sleep(int(count)) + @given('"{issuer}" lists revocation registries {count}') @then('"{issuer}" lists revocation registries {count}') def step_impl(context, issuer, count=None): @@ -34,22 +36,29 @@ def step_impl(context, issuer, count=None): agent["agent"], f"/revocation/registries/created", params={"state": "full"} ) decommissioned_response = agent_container_GET( - agent["agent"], f"/revocation/registries/created", params={"state": "decommissioned"} + agent["agent"], + f"/revocation/registries/created", + params={"state": "decommissioned"}, ) finished_response = agent_container_GET( agent["agent"], f"/revocation/registries/created", params={"state": "finished"} ) async_sleep(4.0) if count: - print(f"\nlists revocation registries ({count} creds) = = = = = = = = = = = = = =") + print( + f"\nlists revocation registries ({count} creds) = = = = = = = = = = = = = =" + ) else: - print("\nlists revocation registries = = = = = = = = = = = = = = = = = = = = = =") + print( + "\nlists revocation registries = = = = = = = = = = = = = = = = = = = = = =" + ) print("\ncreated_response: ", len(created_response["rev_reg_ids"])) print("full_response: ", len(full_response["rev_reg_ids"])) print("decommissioned_response:", len(decommissioned_response["rev_reg_ids"])) print("finished_response: ", len(finished_response["rev_reg_ids"])) async_sleep(1.0) + @given('"{issuer}" rotates revocation registries') @then('"{issuer}" rotates revocation registries') def step_impl(context, issuer): @@ -58,17 +67,17 @@ def step_impl(context, issuer): original_active_response = agent_container_GET( agent["agent"], f"/revocation/active-registry/{cred_def_id}" ) - print("original_active_response:", json.dumps(original_active_response)) - + print("original_active_response:", json.dumps(original_active_response)) + rotate_response = agent_container_POST( agent["agent"], f"/revocation/active-registry/{cred_def_id}/rotate", data={}, ) print("rotate_response:", json.dumps(rotate_response)) - + async_sleep(10.0) - + active_response = agent_container_GET( agent["agent"], f"/revocation/active-registry/{cred_def_id}" ) From 18f15d4d2d8b9b38604c46554b4726bcee256bc5 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 5 Dec 2023 10:08:23 -0800 Subject: [PATCH 4/5] Cleanup integration tests Signed-off-by: Ian Costanzo --- demo/bdd_support/agent_backchannel_client.py | 31 ++++++++++++++- demo/features/revocation-api.feature | 15 +++++--- demo/features/steps/0586-sign-transaction.py | 17 ++++----- demo/runners/agent_container.py | 21 ++++++++-- demo/runners/support/agent.py | 40 +++++++++++++++----- 5 files changed, 95 insertions(+), 29 deletions(-) diff --git a/demo/bdd_support/agent_backchannel_client.py b/demo/bdd_support/agent_backchannel_client.py index a3f99524d2..907f159f75 100644 --- a/demo/bdd_support/agent_backchannel_client.py +++ b/demo/bdd_support/agent_backchannel_client.py @@ -103,16 +103,43 @@ def aries_container_create_schema_cred_def( ) -def aries_container_check_exists_cred_def( +def aries_container_fetch_schemas( + the_container: AgentContainer, +): + return run_coroutine( + the_container.fetch_schemas, + ) + + +def aries_container_fetch_cred_defs( + the_container: AgentContainer, +): + return run_coroutine( + the_container.fetch_cred_defs, + ) + + +def aries_container_fetch_cred_def( the_container: AgentContainer, cred_def_id: str, ): return run_coroutine( - the_container.check_exists_cred_def, + the_container.fetch_cred_def, cred_def_id, ) +def aries_container_check_exists_cred_def( + the_container: AgentContainer, + cred_def_id: str, +): + cred_def = aries_container_fetch_cred_def(the_container, cred_def_id) + if cred_def: + return True + else: + return False + + def aries_container_issue_credential( the_container: AgentContainer, cred_def_id: str, diff --git a/demo/features/revocation-api.feature b/demo/features/revocation-api.feature index 6c8c22a38c..9fcaa2d099 100644 --- a/demo/features/revocation-api.feature +++ b/demo/features/revocation-api.feature @@ -16,7 +16,8 @@ Feature: ACA-Py Revocation API Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | - | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @Revoc-api @GHA Scenario Outline: Using revocation api, issue, revoke credentials and publish @@ -38,7 +39,8 @@ Feature: ACA-Py Revocation API Then "Bob" can verify the credential from "" was revoked Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | - | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @Revoc-api.x @GHA-Anoncreds-break Scenario Outline: Without endorser: issue, revoke credentials, manually create revocation registries @@ -67,7 +69,8 @@ Feature: ACA-Py Revocation API Then "Bob" can verify the credential from "" was revoked Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | - | Acme | --revocation --public-did --did-exchange | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + #| Acme | --revocation --public-did --did-exchange | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + | Acme | --revocation --public-did --did-exchange --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @Revoc-api @GHA Scenario Outline: Using revocation api, rotate revocation @@ -83,7 +86,8 @@ Feature: ACA-Py Revocation API Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | - | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @Revoc-api @GHA Scenario Outline: Using revocation api, fill registry (need to run with "TAILS_FILE_COUNT": "4" env var) @@ -128,4 +132,5 @@ Feature: ACA-Py Revocation API Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | - | Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | + | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index 305217a96e..204eca0cfa 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -10,6 +10,9 @@ async_sleep, read_json_data, read_schema_data, + aries_container_fetch_schemas, + aries_container_fetch_cred_defs, + aries_container_fetch_cred_def, ) from behave import given, then, when from runners.agent_container import AgentContainer @@ -191,7 +194,7 @@ def step_impl(context, agent_name, schema_name): i = 5 while 0 == len(schemas["schema_ids"]) and i > 0: async_sleep(1.0) - schemas = agent_container_GET(agent["agent"], "/schemas/created") + schemas = aries_container_fetch_schemas(agent["agent"]) i = i - 1 assert len(schemas["schema_ids"]) == 1 @@ -211,7 +214,7 @@ def step_impl(context, agent_name, schema_name): connection_id = agent["agent"].agent.connection_id # TODO for now assume there is a single schema; should find the schema based on the supplied name - schemas = agent_container_GET(agent["agent"], "/schemas/created") + schemas = aries_container_fetch_schemas(agent["agent"]) assert len(schemas["schema_ids"]) == 1 schema_id = schemas["schema_ids"][0] @@ -255,16 +258,12 @@ def step_impl(context, agent_name, schema_name): i = 5 while 0 == len(cred_defs["credential_definition_ids"]) and i > 0: async_sleep(1.0) - cred_defs = agent_container_GET( - agent["agent"], "/credential-definitions/created" - ) + cred_defs = aries_container_fetch_cred_defs(agent["agent"]) i = i - 1 assert len(cred_defs["credential_definition_ids"]) == 1 cred_def_id = cred_defs["credential_definition_ids"][0] - cred_def = agent_container_GET( - agent["agent"], "/credential-definitions/" + cred_def_id - ) + cred_def = aries_container_fetch_cred_def(agent["agent"], cred_def_id) context.cred_def_id = cred_def_id @@ -336,7 +335,7 @@ def step_impl(context, agent_name): }, ) i = i - 1 - assert len(rev_regs["rev_reg_ids"]) == 1 + assert len(rev_regs["rev_reg_ids"]) >= 1 rev_reg_id = rev_regs["rev_reg_ids"][0] diff --git a/demo/runners/agent_container.py b/demo/runners/agent_container.py index fa7e0f003c..6dcff17115 100644 --- a/demo/runners/agent_container.py +++ b/demo/runners/agent_container.py @@ -919,15 +919,28 @@ async def create_schema_and_cred_def( else: raise Exception("Invalid credential type:" + self.cred_type) - async def check_exists_cred_def( + async def fetch_schemas( + self, + ): + return await self.agent.fetch_schemas( + wallet_type=self.agent.wallet_type, + ) + + async def fetch_cred_defs( self, - cred_def_id: str, ): - return await self.agent.check_exists_cred_def( - cred_def_id, + return await self.agent.fetch_cred_defs( wallet_type=self.agent.wallet_type, ) + async def fetch_cred_def( + self, + cred_def_id: str, + ): + return await self.agent.fetch_cred_def( + cred_def_id, wallet_type=self.agent.wallet_type + ) + async def issue_credential( self, cred_def_id: str, diff --git a/demo/runners/support/agent.py b/demo/runners/support/agent.py index 9d3ace0274..64a7d1a562 100644 --- a/demo/runners/support/agent.py +++ b/demo/runners/support/agent.py @@ -291,7 +291,35 @@ async def register_schema_and_creddef( else: raise Exception("Invalid wallet_type: " + str(wallet_type)) - async def check_exists_cred_def( + async def fetch_schemas( + self, + wallet_type=WALLET_TYPE_INDY, + ): + if wallet_type in [WALLET_TYPE_INDY, WALLET_TYPE_ASKAR]: + schemas_saved = await self.admin_GET("/schemas/created") + return schemas_saved + elif wallet_type == WALLET_TYPE_ANONCREDS: + schemas_saved = await self.admin_GET("/anoncreds/schemas") + return schemas_saved + else: + raise Exception("Invalid wallet_type: " + str(wallet_type)) + + async def fetch_cred_defs( + self, + wallet_type=WALLET_TYPE_INDY, + ): + if wallet_type in [WALLET_TYPE_INDY, WALLET_TYPE_ASKAR]: + cred_defs_saved = await self.admin_GET("/credential-definitions/created") + return cred_defs_saved + elif wallet_type == WALLET_TYPE_ANONCREDS: + cred_defs_saved = await self.admin_GET("/anoncreds/credential-definitions") + return { + "credential_definition_ids": cred_defs_saved, + } + else: + raise Exception("Invalid wallet_type: " + str(wallet_type)) + + async def fetch_cred_def( self, cred_def_id: str, wallet_type=WALLET_TYPE_INDY, @@ -300,18 +328,12 @@ async def check_exists_cred_def( cred_def_saved = await self.admin_GET( "/credential-definitions/" + cred_def_id ) - if cred_def_saved: - return True - else: - return False + return cred_def_saved elif wallet_type == WALLET_TYPE_ANONCREDS: cred_def_saved = await self.admin_GET( "/anoncreds/credential-definition/" + cred_def_id ) - if cred_def_saved: - return True - else: - return False + return cred_def_saved else: raise Exception("Invalid wallet_type: " + str(wallet_type)) From 75b8bbd49400b0cfa2b422e4a894150f7ebe4b89 Mon Sep 17 00:00:00 2001 From: Ian Costanzo Date: Tue, 5 Dec 2023 10:59:14 -0800 Subject: [PATCH 5/5] Tweak docs Signed-off-by: Ian Costanzo --- AnonCredsWalletType.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AnonCredsWalletType.md b/AnonCredsWalletType.md index 85ee371e9d..2023faed18 100644 --- a/AnonCredsWalletType.md +++ b/AnonCredsWalletType.md @@ -82,7 +82,6 @@ The Tails file changes are minimal -- nothing about the file itself changed. Wh * revocation notifications (not sure if they're included in `anoncreds-rs` updates, haven't tested them ...) * revocation support - complete the revocation implementation (support for unhappy path scenarios) -* endpoints - don't load the schema/cred-def endpoints when wallet type is anoncreds (will require some BDD updates) * testing - various scenarios like mediation, multitenancy etc. - unit tests (in the new anoncreds package) (see https://github.com/hyperledger/aries-cloudagent-python/pull/2596/commits/229ffbba209aff0ea7def5bad6556d93057f3c2a)