Skip to content

Commit

Permalink
Merge pull request #130 from Indicio-tech/feature/anoncreds-pending-r…
Browse files Browse the repository at this point in the history
…evocation

feat(revocation): update revocation to track pending state
  • Loading branch information
dbluhm committed Jun 1, 2023
2 parents ff62b91 + 298790c commit d14199d
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 18 deletions.
204 changes: 187 additions & 17 deletions aries_cloudagent/anoncreds/revocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import asyncio
import hashlib
import http
import json
import logging
import os
from pathlib import Path
Expand All @@ -21,6 +22,7 @@
import base58
from requests import RequestException, Session


from ..askar.profile import AskarProfile, AskarProfileSession
from ..core.error import BaseError
from ..core.profile import Profile
Expand All @@ -47,6 +49,9 @@
CATEGORY_REV_REG_DEF = "revocation_reg_def"
CATEGORY_REV_REG_DEF_PRIVATE = "revocation_reg_def_private"
CATEGORY_REV_REG_ISSUER = "revocation_reg_def_issuer"
STATE_REVOCATION_POSTED = "posted"
STATE_REVOCATION_PENDING = "pending"
REV_REG_DEF_STATE_ACTIVE = "active"


class AnonCredsRevocationError(BaseError):
Expand Down Expand Up @@ -87,7 +92,13 @@ def profile(self) -> AskarProfile:
# Revocation artifact management

async def _finish_registration(
self, txn: AskarProfileSession, category: str, job_id: str, registered_id: str
self,
txn: AskarProfileSession,
category: str,
job_id: str,
registered_id: str,
*,
state: Optional[str] = None,
):
entry = await txn.handle.fetch(
category,
Expand All @@ -99,8 +110,12 @@ async def _finish_registration(
f"{category} with job id {job_id} could not be found"
)

tags = entry.tags
tags["state"] = STATE_FINISHED
if state:
tags = entry.tags
tags["state"] = state
else:
tags = entry.tags

await txn.handle.insert(
category,
registered_id,
Expand Down Expand Up @@ -178,25 +193,33 @@ async def create_and_register_revocation_registry_definition(
self.profile, rev_reg_def, options
)

rev_reg_def_id = result.rev_reg_def_id
rev_reg_def_json = rev_reg_def.to_json()
ident = result.rev_reg_def_id or result.job_id
if not ident:
raise AnonCredsRevocationError(
"Revocation registry definition id or job id not found"
)

# TODO Handle `failed` state

try:
async with self.profile.transaction() as txn:
await txn.handle.insert(
CATEGORY_REV_REG_INFO,
rev_reg_def_id,
value_json={"curr_id": 0, "used_ids": []},
CATEGORY_REV_REG_DEF,
ident,
rev_reg_def.to_json(),
tags={
"cred_def_id": cred_def_id,
"state": result.revocation_registry_definition_state.state,
},
)
await txn.handle.insert(
CATEGORY_REV_REG_DEF,
rev_reg_def_id,
rev_reg_def_json,
tags={"cred_def_id": cred_def_id},
CATEGORY_REV_REG_INFO,
ident,
value_json={"curr_id": 0, "used_ids": []},
)
await txn.handle.insert(
CATEGORY_REV_REG_DEF_PRIVATE,
rev_reg_def_id,
ident,
rev_reg_def_private.to_json_buffer(),
)
await txn.commit()
Expand All @@ -213,7 +236,7 @@ async def finish_revocation_registry_definition(
"""Mark a rev reg def as finished."""
async with self.profile.transaction() as txn:
await self._finish_registration(
txn, CATEGORY_REV_REG_DEF, job_id, rev_reg_def_id
txn, CATEGORY_REV_REG_DEF, job_id, rev_reg_def_id, state=STATE_FINISHED
)
await self._finish_registration(
txn,
Expand Down Expand Up @@ -267,6 +290,61 @@ async def get_created_revocation_registry_definition(

return None

async def set_active_registry(self, cred_def_id: str, rev_reg_def_id: str):
"""Mark a registry as active."""
async with self.profile.transaction() as txn:
entry = await txn.handle.fetch(
CATEGORY_REV_REG_DEF,
rev_reg_def_id,
for_update=True,
)
if not entry:
raise AnonCredsRevocationError(
f"{CATEGORY_REV_REG_DEF} with id "
f"{rev_reg_def_id} could not be found"
)

if entry.tags["active"] == json.dumps(True):
# NOTE If there are other registries set as active, we're not
# clearing them if the one we want to be active is already
# active. This probably isn't an issue.
return

old_active_entries = await txn.handle.fetch_all(
CATEGORY_REV_REG_DEF,
{
"active": json.dumps(True),
"cred_def_id": cred_def_id,
},
for_update=True,
)

if len(old_active_entries) > 1:
LOGGER.error(
"More than one registry was set as active for "
f"cred def {cred_def_id}; clearing active tag from all records"
)

for old_entry in old_active_entries:
tags = old_entry.tags
tags["active"] = json.dumps(False)
await txn.handle.replace(
CATEGORY_REV_REG_DEF,
old_entry.name,
old_entry.value,
tags,
)

tags = entry.tags
tags["active"] = json.dumps(True)
await txn.handle.insert(
CATEGORY_REV_REG_DEF,
rev_reg_def_id,
value=entry.value,
tags=tags,
)
await txn.commit()

async def create_and_register_revocation_list(
self, rev_reg_def_id: str, options: Optional[dict] = None
):
Expand Down Expand Up @@ -301,12 +379,19 @@ async def create_and_register_revocation_list(
self.profile, rev_reg_def, RevList.from_native(rev_list), options
)

# TODO Handle `failed` state

posted = result.revocation_list_state.revocation_list
try:
async with self.profile.session() as session:
await session.handle.insert(
CATEGORY_REV_LIST,
rev_reg_def_id,
result.revocation_list_state.revocation_list.to_json(),
value_json={
"posted": posted.serialize(),
"pending": None,
},
tags={"state": result.revocation_list_state.state},
)
except AskarError as err:
raise AnonCredsRevocationError(
Expand All @@ -315,6 +400,90 @@ async def create_and_register_revocation_list(

return result

async def finish_revocation_list(self, rev_reg_def_id: str):
"""Mark a revocation list as finished."""
async with self.profile.transaction() as txn:
entry = await txn.handle.fetch(
CATEGORY_REV_LIST,
rev_reg_def_id,
for_update=True,
)
if not entry:
raise AnonCredsRevocationError(
f"revocation list with id {rev_reg_def_id} could not be found"
)

await txn.handle.replace(
CATEGORY_REV_LIST,
rev_reg_def_id,
value=entry.value,
tags={"state": STATE_FINISHED},
)
await txn.commit()

async def mark_pending_revocations(self, rev_reg_def_id: str, *crids: int):
async with self.profile.transaction() as txn:
entry = await txn.handle.fetch(
CATEGORY_REV_REG_DEF,
rev_reg_def_id,
)
if not entry:
raise AnonCredsRevocationError(
"Revocation registry definition not found for id {rev_reg_def_id}"
)
rev_reg_def = RevocationRegistryDefinition.load(entry.value)

entry = await txn.handle.fetch(
CATEGORY_REV_LIST,
rev_reg_def_id,
for_update=True,
)

if not entry:
raise AnonCredsRevocationError(
"Revocation list with id {rev_reg_def_id} not found"
)

posted = RevocationStatusList.load(entry.value_json["posted"])
if entry.value_json["pending"]:
pending = RevocationStatusList.load(entry.value_json["pending"])
else:
pending = posted

pending = pending.update(
timestamp=None, issued=None, revoked=crids, rev_reg_def=rev_reg_def
)

await txn.handle.replace(
CATEGORY_REV_LIST,
rev_reg_def_id,
value_json={
"posted": posted.to_dict(),
"pending": pending.to_dict(),
},
)
await txn.commit()

async def clear_pending_revocations(self, rev_reg_def_id: str):
async with self.profile.transaction() as txn:
entry = await txn.handle.fetch(
CATEGORY_REV_LIST,
rev_reg_def_id,
for_update=True,
)

if not entry:
raise AnonCredsRevocationError(
"Revocation list with id {rev_reg_def_id} not found"
)

await txn.handle.replace(
CATEGORY_REV_LIST,
rev_reg_def_id,
value_json={"posted": entry.value_json["posted"], "pending": None},
)
await txn.commit()

async def retrieve_tails(self, rev_reg_def: RevRegDef) -> str:
"""Retrieve tails file from server."""
LOGGER.info(
Expand Down Expand Up @@ -433,14 +602,15 @@ async def get_or_create_active_registry(self, cred_def_id: str) -> RevRegDefResu
CATEGORY_REV_REG_DEF,
{
"cred_def_id": cred_def_id,
"active": json.dumps(True),
},
limit=1,
)

if not rev_reg_defs:
# TODO Create a registry if none available
raise AnonCredsRevocationError("No active registry")

# TODO Sort and return most recent
# TODO Create a registry if none available
entry = rev_reg_defs[0]

rev_reg_def = RevRegDef.deserialize(entry.value_json)
Expand Down
4 changes: 3 additions & 1 deletion aries_cloudagent/indy/credx/issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,9 @@ async def create_and_store_revocation_registry(
value_json={"curr_id": 0, "used_ids": []},
)
await txn.handle.insert(
CATEGORY_REV_REG_DEF, rev_reg_def_id, rev_reg_def_json
CATEGORY_REV_REG_DEF,
rev_reg_def_id,
rev_reg_def_json,
)
await txn.handle.insert(
CATEGORY_REV_REG_DEF_PRIVATE,
Expand Down

0 comments on commit d14199d

Please sign in to comment.