diff --git a/anoncreds_test.py b/anoncreds_test.py index 7845159ded..59b7eecde8 100644 --- a/anoncreds_test.py +++ b/anoncreds_test.py @@ -64,6 +64,12 @@ async def main(): "maxCredNum": 10, }, ) + rev_reg_def_id = rev_reg_def["revocation_registry_definition_state"][ + "revocation_registry_definition_id" + ] + tails = await alice.put( + f"/anoncreds/registry/{rev_reg_def_id}/tails-file", + ) rev_status_list = await alice.post( "/anoncreds/revocation-list", json={ diff --git a/aries_cloudagent/anoncreds/base.py b/aries_cloudagent/anoncreds/base.py index 24fef9c45e..c2d52cd3bd 100644 --- a/aries_cloudagent/anoncreds/base.py +++ b/aries_cloudagent/anoncreds/base.py @@ -12,7 +12,7 @@ ) from .models.anoncreds_revocation import ( GetRevListResult, - AnonCredsRegistryGetRevocationRegistryDefinition, + GetRevRegDefResult, RevRegDef, RevRegDefResult, RevList, @@ -115,7 +115,7 @@ async def get_credential_definition( @abstractmethod async def get_revocation_registry_definition( self, profile: Profile, revocation_registry_id: str - ) -> AnonCredsRegistryGetRevocationRegistryDefinition: + ) -> GetRevRegDefResult: """Get a revocation registry definition from the registry.""" @abstractmethod diff --git a/aries_cloudagent/anoncreds/default/did_indy/registry.py b/aries_cloudagent/anoncreds/default/did_indy/registry.py index a262e00c74..f022b70d41 100644 --- a/aries_cloudagent/anoncreds/default/did_indy/registry.py +++ b/aries_cloudagent/anoncreds/default/did_indy/registry.py @@ -12,7 +12,7 @@ ) from ...models.anoncreds_revocation import ( GetRevListResult, - AnonCredsRegistryGetRevocationRegistryDefinition, + GetRevRegDefResult, RevRegDef, RevRegDefResult, RevList, @@ -70,7 +70,7 @@ async def register_credential_definition( async def get_revocation_registry_definition( self, profile: Profile, revocation_registry_id: str - ) -> AnonCredsRegistryGetRevocationRegistryDefinition: + ) -> GetRevRegDefResult: """Get a revocation registry definition from the registry.""" raise NotImplementedError() diff --git a/aries_cloudagent/anoncreds/default/did_web/registry.py b/aries_cloudagent/anoncreds/default/did_web/registry.py index dea890858b..197a0d2a1b 100644 --- a/aries_cloudagent/anoncreds/default/did_web/registry.py +++ b/aries_cloudagent/anoncreds/default/did_web/registry.py @@ -4,26 +4,15 @@ from ....config.injection_context import InjectionContext from ....core.profile import Profile -from ...models.anoncreds_cred_def import ( - GetCredDefResult, -) -from ...models.anoncreds_revocation import ( - GetRevListResult, - AnonCredsRegistryGetRevocationRegistryDefinition, - RevRegDef, - RevList, - RevListResult, -) -from ...models.anoncreds_schema import GetSchemaResult from ...base import BaseAnonCredsRegistrar, BaseAnonCredsResolver from ...models.anoncreds_cred_def import CredDef, CredDefResult, GetCredDefResult from ...models.anoncreds_revocation import ( - AnonCredsRegistryGetRevocationRegistryDefinition, GetRevListResult, - RevRegDef, - RevRegDefResult, + GetRevRegDefResult, RevList, RevListResult, + RevRegDef, + RevRegDefResult, ) from ...models.anoncreds_schema import AnonCredsSchema, GetSchemaResult, SchemaResult @@ -76,7 +65,7 @@ async def register_credential_definition( async def get_revocation_registry_definition( self, profile: Profile, revocation_registry_id: str - ) -> AnonCredsRegistryGetRevocationRegistryDefinition: + ) -> GetRevRegDefResult: """Get a revocation registry definition from the registry.""" raise NotImplementedError() diff --git a/aries_cloudagent/anoncreds/default/legacy_indy/registry.py b/aries_cloudagent/anoncreds/default/legacy_indy/registry.py index 4385a15095..6063c8152c 100644 --- a/aries_cloudagent/anoncreds/default/legacy_indy/registry.py +++ b/aries_cloudagent/anoncreds/default/legacy_indy/registry.py @@ -19,10 +19,8 @@ IndyLedgerRequestsExecutor, ) from ....multitenant.base import BaseMultitenantManager -from ....revocation.anoncreds import AnonCredsRevocation from ....revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ....revocation.recover import generate_ledger_rrrecovery_txn -from ....storage.error import StorageNotFoundError from ...base import ( AnonCredsObjectAlreadyExists, AnonCredsObjectNotFound, @@ -41,7 +39,7 @@ GetCredDefResult, ) from ...models.anoncreds_revocation import ( - AnonCredsRegistryGetRevocationRegistryDefinition, + GetRevRegDefResult, GetRevListResult, RevRegDef, RevRegDefResult, @@ -49,6 +47,7 @@ RevList, RevListResult, RevListState, + RevRegDefValue, ) from ...models.anoncreds_schema import ( AnonCredsSchema, @@ -357,17 +356,51 @@ async def register_credential_definition( async def get_revocation_registry_definition( self, profile: Profile, rev_reg_def_id: str - ) -> AnonCredsRegistryGetRevocationRegistryDefinition: + ) -> GetRevRegDefResult: """Get a revocation registry definition from the registry.""" + async with profile.session() as session: + multitenant_mgr = session.inject_or(BaseMultitenantManager) + if multitenant_mgr: + ledger_exec_inst = IndyLedgerRequestsExecutor(profile) + else: + ledger_exec_inst = session.inject(IndyLedgerRequestsExecutor) - try: - revoc = AnonCredsRevocation(profile) - rev_reg = await revoc.get_issuer_rev_reg_record(rev_reg_def_id) - except StorageNotFoundError as err: - raise AnonCredsResolutionError(err) + ledger_id, ledger = await ledger_exec_inst.get_ledger_for_identifier( + rev_reg_def_id, + txn_record_type=GET_CRED_DEF, + ) + if not ledger: + reason = "No ledger available" + if not profile.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise AnonCredsResolutionError(reason) + + async with ledger: + rev_reg_def = await ledger.get_revoc_reg_def(rev_reg_def_id) - return rev_reg.serialize - # use AnonCredsRevocationRegistryDefinition object + if rev_reg_def is None: + raise AnonCredsObjectNotFound( + f"Revocation registry definition not found: {rev_reg_def_id}", + {"ledger_id": ledger_id}, + ) + + LOGGER.debug("Retrieved revocation registry definition: %s", rev_reg_def) + rev_reg_def_value = RevRegDefValue.deserialize(rev_reg_def["value"]) + anoncreds_rev_reg_def = RevRegDef( + issuer_id=rev_reg_def["id"].split(":")[0], + cred_def_id=rev_reg_def["credDefId"], + type=rev_reg_def["revocDefType"], + value=rev_reg_def_value, + tag=rev_reg_def["tag"], + ) + result = GetRevRegDefResult( + revocation_registry=anoncreds_rev_reg_def, + revocation_registry_id=rev_reg_def["id"], + resolution_metadata={}, + revocation_registry_metadata={}, + ) + + return result async def get_revocation_registry_definitions(self, profile: Profile, filter: str): """Get credential definition ids filtered by filter""" diff --git a/aries_cloudagent/anoncreds/issuer.py b/aries_cloudagent/anoncreds/issuer.py index 4fad8ddf09..d96c237c63 100644 --- a/aries_cloudagent/anoncreds/issuer.py +++ b/aries_cloudagent/anoncreds/issuer.py @@ -561,7 +561,6 @@ async def create_and_register_revocation_registry_definition( except AnoncredsError as err: raise AnonCredsIssuerError("Error creating revocation registry") from err - rev_reg_def_json = rev_reg_def.to_json() rev_reg_def = RevRegDef.from_native(rev_reg_def) public_tails_uri = self.get_public_tails_uri(rev_reg_def) @@ -572,6 +571,7 @@ async def create_and_register_revocation_registry_definition( ) rev_reg_def_id = result.rev_reg_def_id + rev_reg_def_json = rev_reg_def.to_json() try: async with self._profile.transaction() as txn: @@ -639,13 +639,16 @@ async def upload_tails_file(self, rev_reg_def: RevRegDef): if not upload_success: raise AnonCredsIssuerError( f"Tails file for rev reg for {rev_reg_def.cred_def_id} " - "failed to upload: {result}" + f"failed to upload: {result}" ) if rev_reg_def.value.tails_location != result: raise AnonCredsIssuerError( f"Tails file for rev reg for {rev_reg_def.cred_def_id} " - "uploaded to wrong location: {result}" + f"uploaded to wrong location: {result} " + f"(should have been {rev_reg_def.value.tails_location})" ) + # TODO: do we need to set uri? something like.. + # await self.set_tails_file_public_uri(profile, result) async def update_revocation_registry_definition_state( self, rev_reg_def_id: str, state: str @@ -681,6 +684,22 @@ async def get_created_revocation_registry_definitions( # entry.name was stored as the credential_definition's ID return [entry.name for entry in rev_reg_defs] + async def get_created_revocation_registry_definition( + self, + rev_reg_def_id: str, + ) -> Optional[RevRegDef]: + """Retrieve rev reg def by ID from rev reg defs previously created.""" + async with self._profile.session() as session: + rev_reg_def_entry = await session.handle.fetch( + CATEGORY_REV_REG_DEF, + name=rev_reg_def_id, + ) + + if rev_reg_def_entry: + return RevRegDef.deserialize(rev_reg_def_entry.value_json) + + return None + async def create_and_register_revocation_list( self, rev_reg_def_id: str, options: Optional[dict] = None ): diff --git a/aries_cloudagent/anoncreds/models/anoncreds_revocation.py b/aries_cloudagent/anoncreds/models/anoncreds_revocation.py index 002daa997a..0a88c17740 100644 --- a/aries_cloudagent/anoncreds/models/anoncreds_revocation.py +++ b/aries_cloudagent/anoncreds/models/anoncreds_revocation.py @@ -205,13 +205,13 @@ class Meta: revocation_registry_definition_metadata = fields.Dict() -class AnonCredsRegistryGetRevocationRegistryDefinition(BaseModel): - """AnonCredsRegistryGetRevocationRegistryDefinition""" +class GetRevRegDefResult(BaseModel): + """GetRevRegDefResult""" class Meta: - """AnonCredsRegistryGetRevocationRegistryDefinition metadata.""" + """GetRevRegDefResult metadata.""" - schema_class = "AnonCredsRegistryGetRevocationRegistryDefinitionSchema" + schema_class = "GetRevRegDefResultSchema" def __init__( self, @@ -228,11 +228,11 @@ def __init__( self.revocation_registry_metadata = revocation_registry_metadata -class AnonCredsRegistryGetRevocationRegistryDefinitionSchema(BaseModelSchema): +class GetRevRegDefResultSchema(BaseModelSchema): class Meta: - """AnonCredsRegistryGetRevocationRegistryDefinitionSchema metadata.""" + """GetRevRegDefResultSchema metadata.""" - model_class = AnonCredsRegistryGetRevocationRegistryDefinition + model_class = GetRevRegDefResult unknown = EXCLUDE revocation_registry = fields.Nested(RevRegDefSchema()) diff --git a/aries_cloudagent/anoncreds/registry.py b/aries_cloudagent/anoncreds/registry.py index c3b9b9b6bb..1412471ee3 100644 --- a/aries_cloudagent/anoncreds/registry.py +++ b/aries_cloudagent/anoncreds/registry.py @@ -11,7 +11,7 @@ ) from .models.anoncreds_revocation import ( GetRevListResult, - AnonCredsRegistryGetRevocationRegistryDefinition, + GetRevRegDefResult, RevRegDef, RevRegDefResult, RevList, @@ -119,7 +119,7 @@ async def register_credential_definition( async def get_revocation_registry_definition( self, profile: Profile, revocation_registry_id: str - ) -> AnonCredsRegistryGetRevocationRegistryDefinition: + ) -> GetRevRegDefResult: """Get a revocation registry definition from the registry.""" resolver = await self._resolver_for_identifier(revocation_registry_id) return await resolver.get_revocation_registry_definition( diff --git a/aries_cloudagent/anoncreds/routes.py b/aries_cloudagent/anoncreds/routes.py index 2683ae979e..ca80c2d6fe 100644 --- a/aries_cloudagent/anoncreds/routes.py +++ b/aries_cloudagent/anoncreds/routes.py @@ -11,6 +11,12 @@ response_schema, ) from marshmallow import fields +from aries_cloudagent.askar.profile import AskarProfile + +from aries_cloudagent.revocation.routes import ( + RevRegIdMatchInfoSchema, + RevocationModuleResponseSchema, +) from ..admin.request_context import AdminRequestContext from ..messaging.models.openapi import OpenAPISchema @@ -371,17 +377,18 @@ async def rev_reg_def_post(request: web.BaseRequest): ) try: - revoc = AnonCredsRevocation(context.profile) - issuer_rev_reg_rec = await revoc.init_issuer_registry( - issuer_id, - cred_def_id, - max_cred_num=max_cred_num, - options=options, - notify=False, + result = await shield( + issuer.create_and_register_revocation_registry_definition( + issuer_id, + cred_def_id, + registry_type="CL_ACCUM", + max_cred_num=max_cred_num, + tag="default", + options=options, + ) ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest(reason=e.message) from e - result = await shield(issuer_rev_reg_rec.create_and_register_def(context.profile)) return web.json_response(result.serialize()) @@ -439,6 +446,39 @@ async def rev_list_post(request: web.BaseRequest): return web.json_response(result.serialize()) +@docs( + tags=["revocation"], + summary="Upload local tails file to server", +) +@match_info_schema(RevRegIdMatchInfoSchema()) +@response_schema(RevocationModuleResponseSchema(), description="") +async def upload_tails_file(request: web.BaseRequest): + """ + Request handler to upload local tails file for revocation registry. + + Args: + request: aiohttp request object + + """ + context: AdminRequestContext = request["context"] + profile: AskarProfile = context.profile + rev_reg_id = request.match_info["rev_reg_id"] + try: + issuer = AnonCredsIssuer(profile) + rev_reg_def = await issuer.get_created_revocation_registry_definition( + rev_reg_id + ) + if rev_reg_def is None: + raise web.HTTPNotFound(reason="No rev reg def found") + + await issuer.upload_tails_file(rev_reg_def) + + except AnonCredsIssuerError as e: + raise web.HTTPInternalServerError(reason=str(e)) from e + + return web.json_response({}) + + async def register(app: web.Application): """Register routes.""" @@ -460,6 +500,7 @@ async def register(app: web.Application): ), web.post("/anoncreds/revocation-registry-definition", rev_reg_def_post), web.post("/anoncreds/revocation-list", rev_list_post), + web.put("/anoncreds/registry/{rev_reg_id}/tails-file", upload_tails_file), ] )