From 4362fb8a237cb9283d26454a3b690fd989567a3e Mon Sep 17 00:00:00 2001 From: Jamie Hale Date: Wed, 25 Sep 2024 23:43:32 +0000 Subject: [PATCH] Add did:indy scenario test Signed-off-by: Jamie Hale --- .../docker-compose.yml | 89 +++++++++++++ .../example.py | 122 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 scenarios/examples/did_indy_issuance_and_revocation/docker-compose.yml create mode 100644 scenarios/examples/did_indy_issuance_and_revocation/example.py diff --git a/scenarios/examples/did_indy_issuance_and_revocation/docker-compose.yml b/scenarios/examples/did_indy_issuance_and_revocation/docker-compose.yml new file mode 100644 index 0000000000..67e4424893 --- /dev/null +++ b/scenarios/examples/did_indy_issuance_and_revocation/docker-compose.yml @@ -0,0 +1,89 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url http://test.bcovrin.vonx.io/genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level info + --debug-webhooks + --notify-revocation + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + ports: + - "3002:3001" + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url http://test.bcovrin.vonx.io/genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level info + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=http://test.bcovrin.vonx.io/genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + diff --git a/scenarios/examples/did_indy_issuance_and_revocation/example.py b/scenarios/examples/did_indy_issuance_and_revocation/example.py new file mode 100644 index 0000000000..407de5e2a5 --- /dev/null +++ b/scenarios/examples/did_indy_issuance_and_revocation/example.py @@ -0,0 +1,122 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +import json +from dataclasses import dataclass +from os import getenv + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import V20PresExRecord +from acapy_controller.protocols import ( + DIDResult, + didexchange, + indy_anoncred_credential_artifacts, + indy_anoncreds_publish_revocation, + indy_anoncreds_revoke, + indy_issue_credential_v2, + indy_present_proof_v2, + params, +) +from aiohttp import ClientSession + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +def summary(presentation: V20PresExRecord) -> str: + """Summarize a presentation exchange record.""" + request = presentation.pres_request + return "Summary: " + json.dumps( + { + "state": presentation.state, + "verified": presentation.verified, + "presentation_request": request.dict(by_alias=True) if request else None, + }, + indent=2, + sort_keys=True, + ) + + +@dataclass +class IndyDidCreateResponse: + """Response from creating a DID.""" + + did: str + verkey: str + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + # Connecting + alice_conn, bob_conn = await didexchange(alice, bob) + + # Issuance prep + config = (await alice.get("/status/config"))["config"] + genesis_url = config.get("ledger.genesis_url") + public_did = (await alice.get("/wallet/did/public", response=DIDResult)).result + if not public_did: + public_did = await alice.post( + "/did/indy/create", + json={}, + response=IndyDidCreateResponse, + ) + assert public_did + + async with ClientSession() as session: + register_url = genesis_url.replace("/genesis", "/register") + async with session.post( + register_url, + json={ + "did": public_did.did, + "verkey": public_did.verkey, + "alias": None, + "role": "ENDORSER", + }, + ) as resp: + assert resp.ok + + await alice.post("/wallet/did/public", params=params(did=public_did.did)) + _, cred_def = await indy_anoncred_credential_artifacts( + alice, + ["firstname", "lastname"], + support_revocation=True, + ) + + # Issue a credential + alice_cred_ex, _ = await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + + # Present the the credential's attributes + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + + # Revoke credential + await indy_anoncreds_revoke( + alice, + cred_ex=alice_cred_ex, + holder_connection_id=alice_conn.connection_id, + notify=True, + ) + await indy_anoncreds_publish_revocation(alice, cred_ex=alice_cred_ex) + await bob.record(topic="revocation-notification") + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main())