diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index da95091be4..8ce93c45a8 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -15,8 +15,8 @@ from ....messaging.credential_definitions.util import notify_cred_def_event from ....messaging.schemas.util import notify_schema_event from ....revocation.util import ( - notify_revocation_entry_event, notify_revocation_reg_endorsed_event, + notify_revocation_entry_endorsed_event, ) from ....storage.error import StorageError, StorageNotFoundError from ....transport.inbound.receipt import MessageReceipt @@ -777,30 +777,18 @@ async def endorsed_txn_post_processing( # revocation registry transaction rev_reg_id = ledger_response["result"]["txnMetadata"]["txnId"] meta_data["context"]["rev_reg_id"] = rev_reg_id - auto_create_rev_reg = meta_data["processing"].get( - "auto_create_rev_reg", False + await notify_revocation_reg_endorsed_event( + self._profile, rev_reg_id, meta_data ) - # If "auto_processing" is enabled, also create the revocation entry record - if auto_create_rev_reg: - await notify_revocation_entry_event( - self._profile, rev_reg_id, meta_data - ) - elif ledger_response["result"]["txn"]["type"] == "114": # revocation entry transaction rev_reg_id = ledger_response["result"]["txn"]["data"]["revocRegDefId"] meta_data["context"]["rev_reg_id"] = rev_reg_id - auto_create_rev_reg = meta_data["processing"].get( - "auto_create_rev_reg", False + await notify_revocation_entry_endorsed_event( + self._profile, rev_reg_id, meta_data ) - # If "auto_processing" is enabled, also upload tails file for this registry - if auto_create_rev_reg: - await notify_revocation_reg_endorsed_event( - self._profile, rev_reg_id, meta_data - ) - elif ledger_response["result"]["txn"]["type"] == "1": # write DID to ledger did = ledger_response["result"]["txn"]["data"]["dest"] diff --git a/aries_cloudagent/revocation/indy.py b/aries_cloudagent/revocation/indy.py index 9e4562e88c..e010fe4534 100644 --- a/aries_cloudagent/revocation/indy.py +++ b/aries_cloudagent/revocation/indy.py @@ -43,6 +43,7 @@ async def init_issuer_registry( tag: str = None, create_pending_rev_reg: bool = False, endorser_connection_id: str = None, + notify: bool = True, ) -> "IssuerRevRegRecord": """Create a new revocation registry record for a credential definition.""" multitenant_mgr = self._profile.inject_or(BaseMultitenantManager) @@ -86,12 +87,13 @@ async def init_issuer_registry( if not endorser_connection_id: raise RevocationError(reason="Endorser connection not found") - await notify_revocation_reg_init_event( - self._profile, - record.record_id, - create_pending_rev_reg=create_pending_rev_reg, - endorser_connection_id=endorser_connection_id, - ) + if notify: + await notify_revocation_reg_init_event( + self._profile, + record.record_id, + create_pending_rev_reg=create_pending_rev_reg, + endorser_connection_id=endorser_connection_id, + ) return record diff --git a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py index d95a5f938b..98451dcb3d 100644 --- a/aries_cloudagent/revocation/models/issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/issuer_rev_reg_record.py @@ -67,7 +67,6 @@ class Meta: STATE_INIT = "init" STATE_GENERATED = "generated" STATE_POSTED = "posted" # definition published - STATE_UPLOADED = "uploaded" # tails file uploaded STATE_ACTIVE = "active" # initial entry published, possibly subsequent entries STATE_FULL = "full" # includes corrupt @@ -279,7 +278,7 @@ async def send_entry( self._check_url(self.tails_public_uri) if self.state not in ( - IssuerRevRegRecord.STATE_UPLOADED, + IssuerRevRegRecord.STATE_POSTED, IssuerRevRegRecord.STATE_ACTIVE, IssuerRevRegRecord.STATE_FULL, # can still publish revocation deltas ): @@ -299,7 +298,7 @@ async def send_entry( write_ledger=write_ledger, endorser_did=endorser_did, ) - if self.state == IssuerRevRegRecord.STATE_UPLOADED: + if self.state == IssuerRevRegRecord.STATE_POSTED: self.state = IssuerRevRegRecord.STATE_ACTIVE # initial entry activates async with profile.session() as session: await self.save( @@ -321,7 +320,7 @@ async def upload_tails_file(self, profile: Profile): if not self.has_local_tails_file: raise RevocationError("Local tails file not found") - (upload_success, reason) = await tails_server.upload_tails_file( + (upload_success, result) = await tails_server.upload_tails_file( profile.context, self.revoc_reg_id, self.tails_local_path, @@ -331,12 +330,9 @@ async def upload_tails_file(self, profile: Profile): ) if not upload_success: raise RevocationError( - f"Tails file for rev reg {self.revoc_reg_id} failed to upload: {reason}" + f"Tails file for rev reg {self.revoc_reg_id} failed to upload: {result}" ) - - self.state = IssuerRevRegRecord.STATE_UPLOADED - async with profile.session() as session: - await self.save(session, reason="Uploaded tails file") + await self.set_tails_file_public_uri(profile, result) async def mark_pending(self, session: ProfileSession, cred_rev_id: str) -> None: """Mark a credential revocation id as revoked pending publication to ledger. diff --git a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py index f0347939e4..8154884aeb 100644 --- a/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py +++ b/aries_cloudagent/revocation/models/tests/test_issuer_rev_reg_record.py @@ -65,7 +65,7 @@ async def setUp(self): TailsServer = async_mock.MagicMock(BaseTailsServer, autospec=True) self.tails_server = TailsServer() self.tails_server.upload_tails_file = async_mock.CoroutineMock( - return_value=(True, None) + return_value=(True, "http://1.2.3.4:8088/rev-reg-id") ) self.profile.context.injector.bind_instance(BaseTailsServer, self.tails_server) @@ -126,7 +126,10 @@ async def test_generate_registry_etc(self): with async_mock.patch.object(test_module.Path, "is_file", lambda _: True): await rec.upload_tails_file(self.profile) - assert rec.state == IssuerRevRegRecord.STATE_UPLOADED + assert ( + rec.tails_public_uri + and rec.revoc_reg_def.value.tails_location == rec.tails_public_uri + ) self.tails_server.upload_tails_file.assert_called_once() await rec.send_entry(self.profile) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index b54547d63f..637332d55a 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -550,6 +550,7 @@ async def create_rev_reg(request: web.BaseRequest): issuer_rev_reg_rec = await revoc.init_issuer_registry( credential_definition_id, max_cred_num=max_cred_num, + notify=False, ) except RevocationNotSupportedError as e: raise web.HTTPBadRequest(reason=e.message) from e @@ -1128,17 +1129,16 @@ async def send_rev_reg_entry(request: web.BaseRequest): raise web.HTTPBadRequest(reason="No endorser connection found") if not write_ledger: - try: - async with profile.session() as session: + async with profile.session() as session: + try: connection_record = await ConnRecord.retrieve_by_id( session, connection_id ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except BaseModelError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except BaseModelError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err - async with profile.session() as session: endorser_info = await connection_record.metadata_get( session, "endorser_info" ) @@ -1295,14 +1295,16 @@ async def on_revocation_registry_init_event(profile: Profile, event: Event): meta_data = event.payload if "endorser" in meta_data: # TODO error handling - for now just let exceptions get raised + endorser_connection_id = meta_data["endorser"]["connection_id"] async with profile.session() as session: connection = await ConnRecord.retrieve_by_id( - session, meta_data["endorser"]["connection_id"] + session, endorser_connection_id ) endorser_info = await connection.metadata_get(session, "endorser_info") endorser_did = endorser_info["endorser_did"] write_ledger = False else: + endorser_connection_id = None endorser_did = None write_ledger = True @@ -1313,10 +1315,8 @@ async def on_revocation_registry_init_event(profile: Profile, event: Event): # Generate the registry and upload the tails file async def generate(rr_record: IssuerRevRegRecord) -> dict: await rr_record.generate_registry(profile) - await rr_record.set_tails_file_public_uri( - profile, - f"{tails_base_url}/{registry_record.revoc_reg_id}", - ) + public_uri = tails_base_url.rstrip("/") + f"/{registry_record.revoc_reg_id}" + await rr_record.set_tails_file_public_uri(profile, public_uri) rev_reg_resp = await rr_record.send_def( profile, write_ledger=write_ledger, @@ -1381,6 +1381,7 @@ async def generate(rr_record: IssuerRevRegRecord) -> dict: registry_record.cred_def_id, registry_record.max_cred_num, registry_record.revoc_def_type, + endorser_connection_id=endorser_connection_id, ) @@ -1450,18 +1451,25 @@ async def on_revocation_entry_event(profile: Profile, event: Event): async def on_revocation_registry_endorsed_event(profile: Profile, event: Event): - """Handle revocation tails file event.""" + """Handle revocation registry endorsement event.""" meta_data = event.payload rev_reg_id = meta_data["context"]["rev_reg_id"] revoc = IndyRevocation(profile) registry_record = await revoc.get_issuer_rev_reg_record(rev_reg_id) - # NOTE: if there are multiple pods, then the one processing this - # event may not be the one that generated the tails file. - await registry_record.upload_tails_file(profile) + + if profile.settings.get_value("endorser.auto_request"): + # NOTE: if there are multiple pods, then the one processing this + # event may not be the one that generated the tails file. + await registry_record.upload_tails_file(profile) + + # Post the initial revocation entry + await notify_revocation_entry_event( + profile, registry_record.record_id, meta_data + ) # create a "pending" registry if one is requested # (this is done automatically when creating a credential definition, so that when a - # revocation registry fills up, we ca continue to issue credentials without a + # revocation registry fills up, we can continue to issue credentials without a # delay) create_pending_rev_reg = meta_data["processing"].get( "create_pending_rev_reg", False diff --git a/aries_cloudagent/revocation/util.py b/aries_cloudagent/revocation/util.py index f81e26dc4a..df40a17630 100644 --- a/aries_cloudagent/revocation/util.py +++ b/aries_cloudagent/revocation/util.py @@ -10,7 +10,8 @@ EVENT_LISTENER_PATTERN = re.compile(f"^{REVOCATION_EVENT_PREFIX}(.*)?$") REVOCATION_REG_INIT_EVENT = "REGISTRY_INIT" REVOCATION_REG_ENDORSED_EVENT = "REGISTRY_ENDORSED" -REVOCATION_ENTRY_EVENT = "ENTRY" +REVOCATION_ENTRY_ENDORSED_EVENT = "ENTRY_ENDORSED" +REVOCATION_ENTRY_EVENT = "SEND_ENTRY" REVOCATION_PUBLISHED_EVENT = "published" REVOCATION_CLEAR_PENDING_EVENT = "clear-pending" @@ -50,6 +51,14 @@ async def notify_revocation_reg_endorsed_event( await profile.notify(topic, meta_data) +async def notify_revocation_entry_endorsed_event( + profile: Profile, rev_reg_id: str, meta_data: dict +): + """Send notification for a revocation registry entry endorsement event.""" + topic = f"{REVOCATION_EVENT_PREFIX}{REVOCATION_ENTRY_ENDORSED_EVENT}::{rev_reg_id}" + await profile.notify(topic, meta_data) + + async def notify_revocation_published_event( profile: Profile, rev_reg_id: str, diff --git a/aries_cloudagent/tails/indy_tails_server.py b/aries_cloudagent/tails/indy_tails_server.py index 9f07970a42..0c5ebb6ab4 100644 --- a/aries_cloudagent/tails/indy_tails_server.py +++ b/aries_cloudagent/tails/indy_tails_server.py @@ -1,8 +1,9 @@ """Indy tails server interface class.""" -from typing import Tuple import logging +from typing import Tuple + from ..config.injection_context import InjectionContext from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..utils.http import put_file, PutError @@ -58,17 +59,18 @@ async def upload_tails_file( "tails_server_upload_url setting is not set" ) + upload_url = tails_server_upload_url.rstrip("/") + f"/{rev_reg_id}" + try: - return ( - True, - await put_file( - f"{tails_server_upload_url}/{rev_reg_id}", - {"tails": tails_file_path}, - {"genesis": genesis_transactions}, - interval=interval, - backoff=backoff, - max_attempts=max_attempts, - ), + await put_file( + upload_url, + {"tails": tails_file_path}, + {"genesis": genesis_transactions}, + interval=interval, + backoff=backoff, + max_attempts=max_attempts, ) except PutError as x_put: return (False, x_put.message) + + return True, upload_url diff --git a/aries_cloudagent/tails/tests/test_indy.py b/aries_cloudagent/tails/tests/test_indy.py index ca9d1d6729..65d026a59e 100644 --- a/aries_cloudagent/tails/tests/test_indy.py +++ b/aries_cloudagent/tails/tests/test_indy.py @@ -38,7 +38,9 @@ async def test_upload(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == context.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_indy_sdk(self): profile = InMemoryProfile.test_profile() @@ -68,7 +70,9 @@ async def test_upload_indy_sdk(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == profile.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_indy_vdr(self): profile = InMemoryProfile.test_profile() @@ -98,7 +102,9 @@ async def test_upload_indy_vdr(self): "/tmp/dummy/path", ) assert ok - assert text == "tails-hash" + assert ( + text == profile.settings["tails_server_upload_url"] + "/" + REV_REG_ID + ) async def test_upload_x(self): context = InjectionContext(