Skip to content

Commit 7c2e96b

Browse files
authored
Merge pull request #169 from Indicio-tech/feature/anoncreds-wallet
Extend `presenting_revoked_credential` example to ledger-agnostic endpoints using wallet=askar-anoncreds
2 parents 8958c87 + dcaff26 commit 7c2e96b

File tree

12 files changed

+342
-58
lines changed

12 files changed

+342
-58
lines changed

.github/workflows/code-quality-check.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ name: Code Quality Check
1111
jobs:
1212
format:
1313
name: Format and Lint
14-
runs-on: ubuntu-latest
14+
runs-on: ubuntu-22.04
1515
steps:
1616
- uses: actions/checkout@v4
1717
- uses: psf/[email protected]

.github/workflows/models.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ env:
1313

1414
jobs:
1515
model-gen:
16-
runs-on: ubuntu-latest
16+
runs-on: ubuntu-22.04
1717

1818
steps:
1919
- uses: actions/checkout@v4

.github/workflows/publish.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55

66
jobs:
77
deploy:
8-
runs-on: ubuntu-latest
8+
runs-on: ubuntu-22.04
99
permissions:
1010
id-token: write
1111

@@ -22,7 +22,7 @@ jobs:
2222
run: poetry install
2323
- name: Run tests
2424
run: |
25-
docker-compose run tests
25+
docker compose run tests
2626
- name: Build package
2727
run: |
2828
poetry build

.github/workflows/tests.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ env:
2121
jobs:
2222
test:
2323
name: Tests
24-
runs-on: ubuntu-latest
24+
runs-on: ubuntu-22.04
2525

2626
steps:
2727
- uses: actions/checkout@v4
2828
- name: Run integration tests for protocols
2929
run: |
30-
docker-compose run tests tests/
30+
docker compose run tests tests/
3131
3232
examples:
3333
name: Check examples
34-
runs-on: ubuntu-latest
34+
runs-on: ubuntu-22.04
3535
steps:
3636
- uses: actions/checkout@v4
3737
- name: Install poetry

acapy_controller/controller.py

+10-15
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,7 @@ def params(**kwargs) -> Mapping[str, Any]:
168168
"""Filter out keys with none values from dictionary."""
169169

170170
return {
171-
key: _serialize_param(value)
172-
for key, value in kwargs.items()
173-
if value is not None
171+
key: _serialize_param(value) for key, value in kwargs.items() if value is not None
174172
}
175173

176174

@@ -188,6 +186,7 @@ def __init__(
188186
label: Optional[str] = None,
189187
wallet_id: Optional[str] = None,
190188
subwallet_token: Optional[str] = None,
189+
wallet_type: Optional[str] = None,
191190
headers: Optional[Mapping[str, str]] = None,
192191
event_queue: Optional[Queue[Event]] = None,
193192
):
@@ -202,7 +201,6 @@ def __init__(
202201
self.subwallet_token = subwallet_token
203202
if subwallet_token:
204203
self.headers["Authorization"] = f"Bearer {subwallet_token}"
205-
206204
self._event_queue: Optional[Queue[Event]] = event_queue
207205

208206
self._stack: Optional[AsyncExitStack] = None
@@ -241,6 +239,10 @@ async def setup(self) -> "Controller":
241239
# Get settings
242240
settings = await self.record("settings")
243241
self.label = settings["label"]
242+
243+
# Get wallet type
244+
config = await self.get("/status/config")
245+
self.wallet_type = config["config"]["wallet.type"]
244246
return self
245247

246248
async def shutdown(self, exc_info: Optional[Tuple] = None):
@@ -297,9 +299,7 @@ def _header_filter(headers: Mapping[str, str]):
297299

298300
body = await resp.text()
299301
if resp.ok:
300-
raise ControllerError(
301-
f"Unexpected content type {resp.content_type}: {body}"
302-
)
302+
raise ControllerError(f"Unexpected content type {resp.content_type}: {body}")
303303
raise ControllerError(f"Request failed: {resp.url} {body}")
304304

305305
async def request(
@@ -314,9 +314,7 @@ async def request(
314314
response: Optional[Type[T]] = None,
315315
) -> Union[T, Mapping[str, Any]]:
316316
"""Make an HTTP request."""
317-
async with ClientSession(
318-
base_url=self.base_url, headers=self.headers
319-
) as session:
317+
async with ClientSession(base_url=self.base_url, headers=self.headers) as session:
320318
headers = dict(headers or {})
321319
headers.update(self.headers)
322320

@@ -645,8 +643,7 @@ async def event(
645643
"""Await an event matching a given topic and condition."""
646644
try:
647645
event = await self.event_queue.get(
648-
lambda event: event.topic == topic
649-
and (select(event) if select else True)
646+
lambda event: event.topic == topic and (select(event) if select else True)
650647
)
651648
except asyncio.TimeoutError:
652649
raise ControllerError(
@@ -685,9 +682,7 @@ async def event_with_values(
685682
try:
686683
event = await self.event_queue.get(
687684
lambda event: event.topic == topic
688-
and all(
689-
event.payload.get(key) == value for key, value in values.items()
690-
),
685+
and all(event.payload.get(key) == value for key, value in values.items()),
691686
timeout=timeout,
692687
)
693688
except asyncio.TimeoutError:

acapy_controller/protocols.py

+133-8
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ class ConnRecord(Minimal):
2424
rfc23_state: str
2525

2626

27-
async def trustping(
28-
sender: Controller, conn: ConnRecord, comment: Optional[str] = None
29-
):
27+
async def trustping(sender: Controller, conn: ConnRecord, comment: Optional[str] = None):
3028
"""Send a trustping to the specified connection."""
3129
await sender.post(
3230
f"/connections/{conn.connection_id}/send-ping",
@@ -372,20 +370,76 @@ async def indy_anoncred_onboard(agent: Controller):
372370
return public_did
373371

374372

373+
# Schema
375374
@dataclass
376375
class SchemaResult(Minimal):
377-
"""Result of creating a schema."""
376+
"""Result of creating a schema using /schemas."""
377+
378+
schema_id: str
379+
378380

381+
@dataclass
382+
class SchemaStateAnoncreds(Minimal):
383+
"""schema_state field in SchemaResultAnoncreds."""
384+
385+
state: str
379386
schema_id: str
387+
schema: dict
380388

381389

390+
@dataclass
391+
class SchemaResultAnoncreds(Minimal):
392+
"""Result of creating a schema using /anoncreds/schema."""
393+
394+
schema_state: SchemaStateAnoncreds
395+
schema_metadata: dict
396+
registration_metadata: dict
397+
job_id: Optional[str] = None
398+
399+
@classmethod
400+
def deserialize(cls: Type[MinType], value: Mapping[str, Any]) -> MinType:
401+
"""Deserialize the cred def result record."""
402+
value = dict(value)
403+
value["schema_state"] = SchemaStateAnoncreds.deserialize(value["schema_state"])
404+
return super().deserialize(value)
405+
406+
407+
# CredDefResult
382408
@dataclass
383409
class CredDefResult(Minimal):
384410
"""Result of creating a credential definition."""
385411

386412
credential_definition_id: str
387413

388414

415+
@dataclass
416+
class CredDefStateAnoncreds(Minimal):
417+
"""credential_definition_state field in CredDefResult."""
418+
419+
state: str
420+
credential_definition_id: str
421+
credential_definition: dict
422+
423+
424+
@dataclass
425+
class CredDefResultAnoncreds(Minimal):
426+
"""Result of creating a cred def using /anoncreds/credential-definition."""
427+
428+
credential_definition_state: CredDefStateAnoncreds
429+
credential_definition_metadata: dict
430+
registration_metadata: dict
431+
job_id: Optional[str] = None
432+
433+
@classmethod
434+
def deserialize(cls: Type[MinType], value: Mapping[str, Any]) -> MinType:
435+
"""Deserialize the cred def result record."""
436+
value = dict(value)
437+
value["credential_definition_state"] = CredDefStateAnoncreds.deserialize(
438+
value["credential_definition_state"]
439+
)
440+
return super().deserialize(value)
441+
442+
389443
async def indy_anoncred_credential_artifacts(
390444
agent: Controller,
391445
attributes: List[str],
@@ -394,8 +448,61 @@ async def indy_anoncred_credential_artifacts(
394448
cred_def_tag: Optional[str] = None,
395449
support_revocation: bool = False,
396450
revocation_registry_size: Optional[int] = None,
451+
issuerID: Optional[str] = None,
397452
):
398453
"""Prepare credential artifacts for indy anoncreds."""
454+
# Get wallet type
455+
if agent.wallet_type is None:
456+
raise ControllerError(
457+
"Wallet type not found. Please correctly set up the controller."
458+
)
459+
anoncreds_wallet = agent.wallet_type == "askar-anoncreds"
460+
461+
# If using wallet=askar-anoncreds:
462+
if anoncreds_wallet:
463+
if issuerID is None:
464+
raise ControllerError(
465+
"If using askar-anoncreds wallet, issuerID must be specified."
466+
)
467+
468+
schema = (
469+
await agent.post(
470+
"/anoncreds/schema",
471+
json={
472+
"schema": {
473+
"attrNames": attributes,
474+
"issuerId": issuerID,
475+
"name": schema_name or "minimal-" + token_hex(8),
476+
"version": schema_version or "1.0",
477+
},
478+
},
479+
response=SchemaResultAnoncreds,
480+
)
481+
).schema_state
482+
483+
cred_def = (
484+
await agent.post(
485+
"/anoncreds/credential-definition",
486+
json={
487+
"credential_definition": {
488+
"issuerId": issuerID,
489+
"schemaId": schema.schema_id,
490+
"tag": cred_def_tag or token_hex(8),
491+
},
492+
"options": {
493+
"revocation_registry_size": (
494+
revocation_registry_size if revocation_registry_size else 10
495+
),
496+
"support_revocation": support_revocation,
497+
},
498+
},
499+
response=CredDefResultAnoncreds,
500+
)
501+
).credential_definition_state
502+
503+
return schema, cred_def
504+
505+
# If using wallet=askar
399506
schema = await agent.post(
400507
"/schemas",
401508
json={
@@ -967,6 +1074,13 @@ async def indy_anoncreds_revoke(
9671074
V1.0: V10CredentialExchange
9681075
V2.0: V20CredExRecordDetail.
9691076
"""
1077+
# Get wallet type
1078+
if issuer.wallet_type is None:
1079+
raise ControllerError(
1080+
"Wallet type not found. Please correctly set up the controller."
1081+
)
1082+
anoncreds_wallet = issuer.wallet_type == "askar-anoncreds"
1083+
9701084
if notify and holder_connection_id is None:
9711085
return (
9721086
"If you are going to set notify to True,"
@@ -976,7 +1090,7 @@ async def indy_anoncreds_revoke(
9761090
# Passes in V10CredentialExchange
9771091
if isinstance(cred_ex, V10CredentialExchange):
9781092
await issuer.post(
979-
url="/revocation/revoke",
1093+
url="{}/revocation/revoke".format("/anoncreds" if anoncreds_wallet else ""),
9801094
json={
9811095
"connection_id": holder_connection_id,
9821096
"rev_reg_id": cred_ex.revoc_reg_id,
@@ -990,7 +1104,7 @@ async def indy_anoncreds_revoke(
9901104
# Passes in V20CredExRecordDetail
9911105
elif isinstance(cred_ex, V20CredExRecordDetail) and cred_ex.indy:
9921106
await issuer.post(
993-
url="/revocation/revoke",
1107+
url="{}/revocation/revoke".format("/anoncreds" if anoncreds_wallet else ""),
9941108
json={
9951109
"connection_id": holder_connection_id,
9961110
"rev_reg_id": cred_ex.indy.rev_reg_id,
@@ -1019,9 +1133,18 @@ async def indy_anoncreds_publish_revocation(
10191133
V1.0: V10CredentialExchange
10201134
V2.0: V20CredExRecordDetail.
10211135
"""
1136+
# Get wallet type
1137+
if issuer.wallet_type is None:
1138+
raise ControllerError(
1139+
"Wallet type not found. Please correctly set up the controller."
1140+
)
1141+
anoncreds_wallet = issuer.wallet_type == "askar-anoncreds"
1142+
10221143
if isinstance(cred_ex, V10CredentialExchange):
10231144
await issuer.post(
1024-
url="/revocation/publish-revocations",
1145+
url="{}/revocation/publish-revocations".format(
1146+
"/anoncreds" if anoncreds_wallet else ""
1147+
),
10251148
json={
10261149
"rev_reg_id": cred_ex.revoc_reg_id,
10271150
"cred_rev_id": cred_ex.revocation_id,
@@ -1032,7 +1155,9 @@ async def indy_anoncreds_publish_revocation(
10321155

10331156
elif isinstance(cred_ex, V20CredExRecordDetail) and cred_ex.indy:
10341157
await issuer.post(
1035-
url="/revocation/publish-revocations",
1158+
url="{}/revocation/publish-revocations".format(
1159+
"/anoncreds" if anoncreds_wallet else ""
1160+
),
10361161
json={
10371162
"rev_reg_id": cred_ex.indy.rev_reg_id,
10381163
"cred_rev_id": cred_ex.indy.cred_rev_id,

conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def compose(self, *command: str) -> int:
3535
"""
3636
try:
3737
subprocess.run(
38-
["docker-compose", "-f", self.compose_file, *command],
38+
["docker", "compose", "-f", self.compose_file, *command],
3939
check=True,
4040
)
4141
return 0

0 commit comments

Comments
 (0)