Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Implement anoncreds credential format #901

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-harness-acapy-aip10.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
BUILD_AGENTS: "-a acapy-main"
TEST_AGENTS: "-d acapy-main"
TEST_SCOPE: "-t @AcceptanceTest -t @AIP10,@RFC0211 -t ~@wip -t ~@T004-RFC0211 -t ~@Transport_NoHttpOutbound"
TEST_SCOPE: "-t @AcceptanceTest -t @AIP10,@RFC0211 -t ~@wip -t ~@T004-RFC0211 -t ~@Transport_NoHttpOutbound -t ~@Anoncreds"
REPORT_PROJECT: acapy-aip10
- name: run-send-gen-test-results-secure
if: ${{ steps.run_test_harness.conclusion == 'success' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-harness-acapy-aip20.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
BUILD_AGENTS: "-a acapy-main"
TEST_AGENTS: "-d acapy-main"
TEST_SCOPE: "-t @AcceptanceTest -t @AIP20 -t ~@wip -t ~@T004-RFC0211 -t ~@Transport_NoHttpOutbound -t ~@DidMethod_orb"
TEST_SCOPE: "-t @AcceptanceTest -t @AIP20 -t ~@wip -t ~@T004-RFC0211 -t ~@Transport_NoHttpOutbound -t ~@DidMethod_orb -t ~@Anoncreds"
REPORT_PROJECT: acapy-aip20
- name: run-send-gen-test-results-secure
if: ${{ steps.run_test_harness.conclusion == 'success' }}
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/test-harness-acapy-anoncreds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: test-harness-acapy-acapy
# RUNSET_NAME: "ACA-PY to ACA-Py"
# Scope: AIP 1.0
# Exceptions: None
# SKIP
#
# Summary
#
# This runset uses the current main branch of ACA-Py for all of the agents. The runset runs all of the tests in the suite
# that are expected to pass given the current state of ACA-Py support for AIP 1 and 2.
#
# Current
#
# All of the tests being executed in this runset are passing.
#
# *Status Note Updated: 2021.03.18*
#
# End
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
test:
runs-on: ubuntu-latest
env:
LEDGER_URL_CONFIG: "http://localhost:9000"
TAILS_SERVER_URL_CONFIG: "http://localhost:6543"
steps:
- name: checkout-test-harness
uses: actions/checkout@v4
with:
path: test-harness
- name: run-von-network
uses: ./test-harness/actions/run-von-network
- name: run-indy-tails-server
uses: ./test-harness/actions/run-indy-tails-server
- name: run-test-harness-wo-reports
id: run_test_harness
uses: ./test-harness/actions/run-test-harness-wo-reports
with:
BUILD_AGENTS: "-a acapy-main"
TEST_AGENTS: "-d acapy-main"
TEST_SCOPE: "-t @AcceptanceTest -t ~@wip -t ~@T004-RFC0211 -t ~@DidMethod_orb -t ~@Transport_NoHttpOutbound -t ~@Indy -t ~@CredFormat_Indy"
REPORT_PROJECT: acapy
- name: run-send-gen-test-results-secure
if: ${{ steps.run_test_harness.conclusion == 'success' }}
uses: ./test-harness/actions/run-send-gen-test-results-secure
with:
REPORT_PROJECT: acapy-anoncreds
ADMIN_USER: ${{ secrets.AllureAdminUser }}
ADMIN_PW: ${{ secrets.AllureAdminPW }}
BACKCHANNEL_EXTRA_acapy_main: "{\"wallet-type\":\"askar-anoncreds\"}"
2 changes: 1 addition & 1 deletion .github/workflows/test-harness-acapy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
with:
BUILD_AGENTS: "-a acapy-main"
TEST_AGENTS: "-d acapy-main"
TEST_SCOPE: "-t @AcceptanceTest -t ~@wip -t ~@T004-RFC0211 -t ~@DidMethod_orb -t ~@Transport_NoHttpOutbound"
TEST_SCOPE: "-t @AcceptanceTest -t ~@wip -t ~@T004-RFC0211 -t ~@DidMethod_orb -t ~@Transport_NoHttpOutbound -t ~@Anoncreds"
REPORT_PROJECT: acapy
- name: run-send-gen-test-results-secure
if: ${{ steps.run_test_harness.conclusion == 'success' }}
Expand Down
1 change: 1 addition & 0 deletions aries-backchannels/acapy/Dockerfile.acapy-main
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ RUN chmod +x ./jq
COPY python/requirements.txt python/
COPY acapy/requirements-main.txt acapy/
RUN pip install -r python/requirements.txt -r acapy/requirements-main.txt
RUN pip install git+https://github.com/jamshale/acapy-plugins@load-connection-didcomm#subdirectory=connections

# Copy the necessary files from the AATH Backchannel sub-folders
COPY python python
Expand Down
82 changes: 71 additions & 11 deletions aries-backchannels/acapy/acapy_backchannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ def __init__(
"proof-v2": "/present-proof-2.0/",
}

self.credFormatFilterTranslationDict = {"indy": "indy", "json-ld": "ld_proof"}
self.credFormatFilterTranslationDict = {
"indy": "indy",
"json-ld": "ld_proof",
"anoncreds": "anoncreds",
}

self.proofTypeKeyTypeTranslationDict = {
"Ed25519Signature2018": "ed25519",
Expand Down Expand Up @@ -178,6 +182,13 @@ def __init__(
"active": "completed",
}

def is_wallet_anoncreds(self):
return self.wallet_type == "askar-anoncreds"

def use_anoncreds(self, command):
anoncreds = self.is_wallet_anoncreds() or command.anoncreds
return anoncreds

def get_acapy_version_as_float(self):
# construct some number to compare to with > or < instead of listing out the version number
# if it starts with zero strip it off
Expand Down Expand Up @@ -230,6 +241,8 @@ def get_agent_args(self):
"--open-mediation",
"--enable-undelivered-queue",
"--preserve-exchange-records", # For AATH purposes, exchange records must be retained -- not typical in production
"--plugin",
"connections",
]

# Backchannel needs to handle operations that may be called in the protocol by the tests
Expand All @@ -256,6 +269,7 @@ def get_agent_args(self):
result.append(("--genesis-transactions", self.genesis_data))
if self.seed:
result.append(("--seed", self.seed))
# deprecated - should be removed
if self.storage_type:
result.append(("--storage-type", self.storage_type))
if self.postgres:
Expand Down Expand Up @@ -300,6 +314,8 @@ def get_agent_args(self):
# when it does (and there is talk of supporting YAML) then this code can be removed.
if os.getenv("LOG_LEVEL") is not None:
result.append(("--log-level", os.getenv("LOG_LEVEL")))
else:
result.append(("--log-level", "ERROR"))

# aca-py supports a config.yaml file to pass in arguments. This env var point to such a file.
if os.getenv("AGENT_CONFIG_FILE") is not None:
Expand Down Expand Up @@ -493,9 +509,10 @@ async def handle_out_of_band(self, message: Mapping[str, Any]):
push_resource(invitation_id, "out-of-band-msg", message)

async def handle_problem_report(self, message: Mapping[str, Any]):
thread_id = message["thread_id"]
push_resource(thread_id, "problem-report-msg", message)
log_msg("Received Problem Report Webhook message: " + json.dumps(message, indent=4))
thread_id = message.get("thread_id")
if thread_id:
push_resource(thread_id, "problem-report-msg", message)

async def swap_thread_id_for_exchange_id(
self,
Expand Down Expand Up @@ -687,8 +704,24 @@ async def make_agent_POST_request(
return (resp_status, resp_text)

elif command.topic == "schema":
# check command type vs agent wallet type - can we support "anoncreds" tests?
if self.use_anoncreds(command):
if not self.is_wallet_anoncreds():
return (400, "Bad request - agent wallet cannot support anoncreds format")

# POST operation is to create a new schema
if command.anoncreds:
if self.use_anoncreds(command):
# make sure our "data" is in the correct format
if not "schema" in data:
new_data = {
"schema": {
"issuerId": data.get("issuer_id"),
"name": data.get("schema_name"),
"version": data.get("schema_version"),
"attrNames": data.get("attributes"),
}
}
data = new_data
schema_name = data['schema'].get("name")
schema_version = data['schema'].get("version")
schema_get_endpoint = '/anoncreds/schemas'
Expand All @@ -698,6 +731,9 @@ async def make_agent_POST_request(
schema_version = data.get("schema_version")
schema_get_endpoint = '/schemas/created'
schema_post_endpoint = '/schemas'
# not needed for "legacy" indy schemas
if "issuer_id" in data:
del data["issuer_id"]

# Check if schema id already exists
log_msg(schema_post_endpoint, data)
Expand All @@ -720,8 +756,25 @@ async def make_agent_POST_request(
return (resp_status, resp_text)

elif command.topic == "credential-definition":
# check command type vs agent wallet type - can we support "anoncreds" tests?
if self.use_anoncreds(command):
if not self.is_wallet_anoncreds():
return (400, "Bad request - agent wallet cannot support anoncreds format")

# POST operation is to create a new cred def
if command.anoncreds:
if self.use_anoncreds(command):
if not "credential_definition" in data:
new_data = {
"credential_definition": {
"schemaId": data.get("schema_id"),
"issuerId": data.get("issuer_id"),
"tag": data.get("tag"),
"options": {
"support_revocation": data.get("support_revocation"),
},
}
}
data = new_data
schema_id = data['credential_definition'].get("schemaId")
tag = data['credential_definition'].get("tag")
cred_defs_get_endpoint = '/anoncreds/credential-definitions'
Expand All @@ -731,6 +784,9 @@ async def make_agent_POST_request(
schema_id = data.get("schema_id")
cred_defs_get_endpoint = '/credential-definitions/created'
cred_defs_post_endpoint = '/credential-definitions'
# not needed for "legacy" indy cred defs
if "issuer_id" in data:
del data["issuer_id"]

agent_operation = "/credential-definitions"
log_msg(agent_operation, json.dumps(command.data, indent=4))
Expand Down Expand Up @@ -1201,6 +1257,8 @@ async def handle_issue_credential_v2_POST(self, command: BackchannelCommand):
record_id = command.record_id
data = command.data

log_msg(">>> got issue_cred message:", operation, topic, json.dumps(data, indent=4))

if (
self.auto_respond_credential_proposal
and operation == "send-offer"
Expand Down Expand Up @@ -1325,6 +1383,8 @@ async def handle_proof_v2_POST(self, command: BackchannelCommand):
record_id = command.record_id
data = command.data

log_msg(f">>> in handle_proof_v2_POST(): {operation} {topic} {record_id} {data}")

if (
self.auto_respond_presentation_proposal
and operation == "send-request"
Expand Down Expand Up @@ -1494,7 +1554,7 @@ async def make_agent_GET_request(

elif command.topic == "schema":
schema_id = record_id
if command.anoncreds:
if self.use_anoncreds(command):
agent_operation = f"/anoncreds/schema/{schema_id}"
else:
agent_operation = f"/schemas/{schema_id}"
Expand All @@ -1507,7 +1567,7 @@ async def make_agent_GET_request(
schema = resp_json["schema"]

# If anoncreds, add the id to the schema to use existing framework
if command.anoncreds:
if self.use_anoncreds(command):
schema["id"] = resp_json["schema_id"]

resp_text = json.dumps(schema)
Expand All @@ -1516,7 +1576,7 @@ async def make_agent_GET_request(
elif command.topic == "credential-definition":
cred_def_id = record_id

if command.anoncreds:
if self.use_anoncreds(command):
agent_operation = f"/anoncreds/credential-definition/{cred_def_id}"
else:
agent_operation = f"/credential-definitions/{cred_def_id}"
Expand All @@ -1529,7 +1589,7 @@ async def make_agent_GET_request(
credential_definition = resp_json["credential_definition"]

# If anoncreds, add the id to the credential definition to use existing framework
if command.anoncreds:
if self.use_anoncreds(command):
credential_definition["id"] = resp_json["credential_definition_id"]

resp_text = json.dumps(credential_definition)
Expand Down Expand Up @@ -2082,7 +2142,7 @@ def map_test_json_to_admin_api_json(

if cred_format is None:
raise Exception("Credential format not specified for presentation")
elif cred_format == "indy":
elif cred_format == "indy" or cred_format == "anoncreds":
requested_attributes = pres_request_data.get(
"requested_attributes", {}
)
Expand Down Expand Up @@ -2127,7 +2187,7 @@ def map_test_json_to_admin_api_json(

if cred_format is None:
raise Exception("Credential format not specified for presentation")
elif cred_format == "indy":
elif cred_format == "indy" or cred_format == "anoncreds":
requested_attributes = data.get("requested_attributes", {})
requested_predicates = data.get("requested_predicates", {})
self_attested_attributes = data.get("self_attested_attributes", {})
Expand Down
2 changes: 1 addition & 1 deletion aries-backchannels/python/agent_backchannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ async def backchannel_middleware(request: web.Request, handler: Handler):

self.admin_url = f"http://{self.internal_host}:" + str(agent_ports["admin"])

self.storage_type = "askar"
self.storage_type = "askar" # deprecated - should be removed
self.wallet_type = (
extra_args.get("wallet-type") if extra_args.get("wallet-type") else "askar"
)
Expand Down
24 changes: 23 additions & 1 deletion aries-test-harness/agent_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def format_cred_proposal_by_aip_version(
filters = amend_filters_with_runtime_data(context, filters, did_for_id)
credential_proposal = {
"credential_preview": {
"@type": "issue-credential/2.0/credential-preview",
"@type": "https://didcomm.org/issue-credential/2.0/credential-preview",
"attributes": cred_data,
},
"filter": filters,
Expand Down Expand Up @@ -105,6 +105,28 @@ def amend_filters_with_runtime_data(context, filters, did_for_id=None):
):
filters["indy"]["schema_id"] = context.issuer_schema_dict[schema_name]["id"]

if "anoncreds" in filters:
if (
"schema_issuer_did" in filters["anoncreds"]
and filters["anoncreds"]["schema_issuer_did"] == "replace_me"
):
filters["anoncreds"]["schema_issuer_did"] = context.issuer_did_dict[schema_name]
if (
"issuer_did" in filters["anoncreds"]
and filters["anoncreds"]["issuer_did"] == "replace_me"
):
filters["anoncreds"]["issuer_did"] = context.issuer_did_dict[schema_name]
if (
"cred_def_id" in filters["anoncreds"]
and filters["anoncreds"]["cred_def_id"] == "replace_me"
):
filters["anoncreds"]["cred_def_id"] = context.issuer_credential_definition_dict[schema_name]["id"]
if (
"schema_id" in filters["anoncreds"]
and filters["anoncreds"]["schema_id"] == "replace_me"
):
filters["anoncreds"]["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")
Expand Down
4 changes: 2 additions & 2 deletions aries-test-harness/features/0036-issue-credential.feature
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Feature: RFC 0036 Aries agent issue credential
And "Bob" acknowledges the credential issue
Then "Bob" has the credential issued

@T005-RFC0036 @AIP10 @minor @wip @AcceptanceTest
@T005-RFC0036 @AIP10 @minor @wip @AcceptanceTest @Indy
Scenario: Issue a credential with negotiation beginning from a credential request
Given "2" agents
| name | role |
Expand All @@ -92,7 +92,7 @@ Feature: RFC 0036 Aries agent issue credential
And "Bob" acknowledges the credential issue
Then "Bob" has the credential issued

@T006-RFC0036 @AIP10 @critical @wip @AcceptanceTest
@T006-RFC0036 @AIP10 @critical @wip @AcceptanceTest @Indy
Scenario: Issue a credential with the Holder beginning with a request and is accepted
Given "2" agents
| name | role |
Expand Down
2 changes: 1 addition & 1 deletion aries-test-harness/features/0037-present-proof.feature
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Feature: RFC 0037 Aries agent present proof
| Faber | Data_BI_HealthValues | proof_request_health_consent | presentation_health_consent |


@T001.5-RFC0037 @AIP10 @critical @AcceptanceTest @MobileTest
@T001.5-RFC0037 @AIP10 @critical @AcceptanceTest @MobileTest @Indy
Scenario Outline: Present Proof where the prover does not propose a presentation of the proof and is acknowledged
Given "2" agents
| name | role |
Expand Down
Loading