diff --git a/aries_cloudagent/holder/routes.py b/aries_cloudagent/holder/routes.py index 07067c41bb..7d52bdfc7d 100644 --- a/aries_cloudagent/holder/routes.py +++ b/aries_cloudagent/holder/routes.py @@ -174,9 +174,8 @@ async def credentials_get(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] credential_id = request.match_info["credential_id"] - session = await context.session() - holder = session.inject(IndyHolder) + holder = context.profile.inject(IndyHolder) try: credential = await holder.get_credential(credential_id) except WalletNotFoundError as err: @@ -202,31 +201,31 @@ async def credentials_revoked(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - session = await context.session() credential_id = request.match_info["credential_id"] fro = request.query.get("from") to = request.query.get("to") - ledger = session.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise web.HTTPForbidden(reason=reason) - - async with ledger: - try: - holder = session.inject(IndyHolder) - revoked = await holder.credential_revoked( - ledger, - credential_id, - int(fro) if fro else None, - int(to) if to else None, - ) - except WalletNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except LedgerError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + async with context.profile.session() as session: + ledger = session.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise web.HTTPForbidden(reason=reason) + + async with ledger: + try: + holder = session.inject(IndyHolder) + revoked = await holder.credential_revoked( + ledger, + credential_id, + int(fro) if fro else None, + int(to) if to else None, + ) + except WalletNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except LedgerError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response({"revoked": revoked}) @@ -246,11 +245,12 @@ async def credentials_attr_mime_types_get(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - session = await context.session() credential_id = request.match_info["credential_id"] - holder = session.inject(IndyHolder) - return web.json_response({"results": await holder.get_mime_type(credential_id)}) + async with context.profile.session() as session: + holder = session.inject(IndyHolder) + mime_types = await holder.get_mime_type(credential_id) + return web.json_response({"results": mime_types}) @docs(tags=["credentials"], summary="Remove credential from wallet by id") @@ -270,10 +270,10 @@ async def credentials_remove(request: web.BaseRequest): context: AdminRequestContext = request["context"] credential_id = request.match_info["credential_id"] - session = await context.session() - holder = session.inject(IndyHolder) try: - await holder.delete_credential(credential_id) + async with context.profile.session() as session: + holder = session.inject(IndyHolder) + await holder.delete_credential(credential_id) except WalletNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err @@ -298,7 +298,6 @@ async def credentials_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - session = await context.session() start = request.query.get("start") count = request.query.get("count") @@ -310,11 +309,12 @@ async def credentials_list(request: web.BaseRequest): start = int(start) if isinstance(start, str) else 0 count = int(count) if isinstance(count, str) else 10 - holder = session.inject(IndyHolder) - try: - credentials = await holder.get_credentials(start, count, wql) - except IndyHolderError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + async with context.profile.session() as session: + holder = session.inject(IndyHolder) + try: + credentials = await holder.get_credentials(start, count, wql) + except IndyHolderError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response({"results": credentials}) @@ -339,14 +339,14 @@ async def w3c_cred_get(request: web.BaseRequest): context: AdminRequestContext = request["context"] credential_id = request.match_info["credential_id"] - session = await context.session() - holder = session.inject(VCHolder) - try: - vc_record = await holder.retrieve_credential_by_id(credential_id) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + async with context.profile.session() as session: + holder = session.inject(VCHolder) + try: + vc_record = await holder.retrieve_credential_by_id(credential_id) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(vc_record.serialize()) @@ -371,15 +371,15 @@ async def w3c_cred_remove(request: web.BaseRequest): context: AdminRequestContext = request["context"] credential_id = request.match_info["credential_id"] - session = await context.session() - holder = session.inject(VCHolder) - try: - vc_record = await holder.retrieve_credential_by_id(credential_id) - await holder.delete_credential(vc_record) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + async with context.profile.session() as session: + holder = session.inject(VCHolder) + try: + vc_record = await holder.retrieve_credential_by_id(credential_id) + await holder.delete_credential(vc_record) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response({}) @@ -403,7 +403,6 @@ async def w3c_creds_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - session = await context.session() body = await request.json() contexts = body.get("contexts") types = body.get("types") @@ -415,23 +414,24 @@ async def w3c_creds_list(request: web.BaseRequest): tag_query = body.get("tag_query") max_results = body.get("max_results") - holder = session.inject(VCHolder) - try: - search = holder.search_credentials( - contexts=contexts, - types=types, - schema_ids=schema_ids, - issuer_id=issuer_id, - subject_ids=subject_ids, - proof_types=proof_types, - given_id=given_id, - tag_query=tag_query, - ) - records = await search.fetch(max_results) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err + async with context.profile.session() as session: + holder = session.inject(VCHolder) + try: + search = holder.search_credentials( + contexts=contexts, + types=types, + schema_ids=schema_ids, + issuer_id=issuer_id, + subject_ids=subject_ids, + proof_types=proof_types, + given_id=given_id, + tag_query=tag_query, + ) + records = await search.fetch(max_results) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response({"results": [record.serialize() for record in records]}) diff --git a/aries_cloudagent/holder/tests/test_routes.py b/aries_cloudagent/holder/tests/test_routes.py index 58d9dbb709..322e4180bf 100644 --- a/aries_cloudagent/holder/tests/test_routes.py +++ b/aries_cloudagent/holder/tests/test_routes.py @@ -3,6 +3,7 @@ from asynctest import mock as async_mock, TestCase as AsyncTestCase from ...config.injection_context import InjectionContext +from ...core.in_memory import InMemoryProfile from ...ledger.base import BaseLedger from ...wallet.base import BaseWallet @@ -35,8 +36,9 @@ class TestHolderRoutes(AsyncTestCase): def setUp(self): - self.context = AdminRequestContext.test_context() - + self.profile = InMemoryProfile.test_profile() + self.context = self.profile.context + setattr(self.context, "profile", self.profile) self.request_dict = {"context": self.context} self.request = async_mock.MagicMock( app={}, @@ -47,7 +49,7 @@ def setUp(self): async def test_credentials_get(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( get_credential=async_mock.CoroutineMock( @@ -65,7 +67,7 @@ async def test_credentials_get(self): async def test_credentials_get_not_found(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( get_credential=async_mock.CoroutineMock( @@ -79,10 +81,10 @@ async def test_credentials_get_not_found(self): async def test_credentials_revoked(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( BaseLedger, async_mock.create_autospec(BaseLedger) ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( credential_revoked=async_mock.CoroutineMock(return_value=False) @@ -104,10 +106,10 @@ async def test_credentials_revoked_no_ledger(self): async def test_credentials_not_found(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( BaseLedger, async_mock.create_autospec(BaseLedger) ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( credential_revoked=async_mock.CoroutineMock( @@ -122,10 +124,10 @@ async def test_credentials_not_found(self): async def test_credentials_x_ledger(self): self.request.match_info = {"credential_id": "dummy"} ledger = async_mock.create_autospec(BaseLedger) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( BaseLedger, async_mock.create_autospec(BaseLedger) ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( credential_revoked=async_mock.CoroutineMock( @@ -139,7 +141,7 @@ async def test_credentials_x_ledger(self): async def test_attribute_mime_types_get(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( get_mime_type=async_mock.CoroutineMock( @@ -160,7 +162,7 @@ async def test_attribute_mime_types_get(self): async def test_credentials_remove(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( delete_credential=async_mock.CoroutineMock(return_value=None) @@ -176,7 +178,7 @@ async def test_credentials_remove(self): async def test_credentials_remove_not_found(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( delete_credential=async_mock.CoroutineMock( @@ -189,7 +191,7 @@ async def test_credentials_remove_not_found(self): async def test_credentials_list(self): self.request.query = {"start": "0", "count": "10"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( get_credentials=async_mock.CoroutineMock( @@ -207,7 +209,7 @@ async def test_credentials_list(self): async def test_credentials_list_x_holder(self): self.request.query = {"start": "0", "count": "10"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( IndyHolder, async_mock.MagicMock( get_credentials=async_mock.CoroutineMock( @@ -221,7 +223,7 @@ async def test_credentials_list_x_holder(self): async def test_w3c_cred_get(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -238,7 +240,7 @@ async def test_w3c_cred_get(self): async def test_w3c_cred_get_not_found_x(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -252,7 +254,7 @@ async def test_w3c_cred_get_not_found_x(self): async def test_w3c_cred_get_storage_x(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -266,7 +268,7 @@ async def test_w3c_cred_get_storage_x(self): async def test_w3c_cred_remove(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -285,7 +287,7 @@ async def test_w3c_cred_remove(self): async def test_w3c_cred_remove_not_found_x(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -299,7 +301,7 @@ async def test_w3c_cred_remove_not_found_x(self): async def test_w3c_cred_remove_storage_x(self): self.request.match_info = {"credential_id": "dummy"} - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( retrieve_credential_by_id=async_mock.CoroutineMock( @@ -326,7 +328,7 @@ async def test_w3c_creds_list(self): "max_results": "1", } ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( search_credentials=async_mock.MagicMock( @@ -355,7 +357,7 @@ async def test_w3c_creds_list_not_found_x(self): "max_results": "1", } ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( search_credentials=async_mock.MagicMock( @@ -383,7 +385,7 @@ async def test_w3c_creds_list_storage_x(self): "max_results": "1", } ) - self.context.injector.bind_instance( + self.profile.context.injector.bind_instance( VCHolder, async_mock.MagicMock( search_credentials=async_mock.MagicMock( diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index 249f6a7276..8dad66cca1 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -20,6 +20,7 @@ ) from ...askar.profile import AskarProfile +from ...core.profile import ProfileSession from ..issuer import ( IndyIssuer, @@ -375,7 +376,11 @@ async def create_credential( return credential.to_json(), credential_revocation_id async def revoke_credentials( - self, revoc_reg_id: str, tails_file_path: str, cred_revoc_ids: Sequence[str] + self, + revoc_reg_id: str, + tails_file_path: str, + cred_revoc_ids: Sequence[str], + transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. @@ -390,7 +395,7 @@ async def revoke_credentials( """ - txn = await self._profile.transaction() + txn = transaction if transaction else await self._profile.transaction() try: rev_reg_def = await txn.handle.fetch(CATEGORY_REV_REG_DEF, revoc_reg_id) rev_reg = await txn.handle.fetch( @@ -471,7 +476,8 @@ async def revoke_credentials( await txn.handle.replace( CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info ) - await txn.commit() + if not transaction: + await txn.commit() except AskarError as err: raise IndyIssuerError("Error saving revocation registry") from err else: diff --git a/aries_cloudagent/indy/issuer.py b/aries_cloudagent/indy/issuer.py index 4848508933..05c538cb43 100644 --- a/aries_cloudagent/indy/issuer.py +++ b/aries_cloudagent/indy/issuer.py @@ -4,6 +4,7 @@ from typing import Sequence, Tuple from ..core.error import BaseError +from ..core.profile import ProfileSession DEFAULT_CRED_DEF_TAG = "default" @@ -145,7 +146,11 @@ async def create_credential( @abstractmethod async def revoke_credentials( - self, revoc_reg_id: str, tails_file_path: str, cred_rev_ids: Sequence[str] + self, + revoc_reg_id: str, + tails_file_path: str, + cred_rev_ids: Sequence[str], + transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. diff --git a/aries_cloudagent/indy/sdk/issuer.py b/aries_cloudagent/indy/sdk/issuer.py index d60cb7631a..2298143e61 100644 --- a/aries_cloudagent/indy/sdk/issuer.py +++ b/aries_cloudagent/indy/sdk/issuer.py @@ -8,6 +8,7 @@ import indy.blob_storage from indy.error import AnoncredsRevocationRegistryFullError, IndyError, ErrorCode +from ...core.profile import ProfileSession from ...indy.sdk.profile import IndySdkProfile from ...messaging.util import encode from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord @@ -262,7 +263,11 @@ async def create_credential( return (credential_json, cred_rev_id) async def revoke_credentials( - self, rev_reg_id: str, tails_file_path: str, cred_rev_ids: Sequence[str] + self, + rev_reg_id: str, + tails_file_path: str, + cred_rev_ids: Sequence[str], + transaction: ProfileSession = None, ) -> Tuple[str, Sequence[str]]: """ Revoke a set of credentials in a revocation registry. diff --git a/aries_cloudagent/ledger/indy_vdr.py b/aries_cloudagent/ledger/indy_vdr.py index f4c05c8ea1..c126253de5 100644 --- a/aries_cloudagent/ledger/indy_vdr.py +++ b/aries_cloudagent/ledger/indy_vdr.py @@ -1353,6 +1353,9 @@ async def txn_submit( sign_did: DIDInfo = sentinel, ) -> str: """Write the provided (signed and possibly endorsed) transaction to the ledger.""" - return await self._submit( + resp = await self._submit( request_json, sign=sign, taa_accept=taa_accept, sign_did=sign_did ) + # match the format returned by indy sdk + sdk_resp = {"op": "REPLY", "result": resp} + return json.dumps(sdk_resp) diff --git a/aries_cloudagent/messaging/credential_definitions/routes.py b/aries_cloudagent/messaging/credential_definitions/routes.py index 68ece34321..77e64923fa 100644 --- a/aries_cloudagent/messaging/credential_definitions/routes.py +++ b/aries_cloudagent/messaging/credential_definitions/routes.py @@ -266,12 +266,11 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq return web.json_response({"credential_definition_id": cred_def_id}) else: - session = await context.session() - meta_data["processing"][ - "auto_create_rev_reg" - ] = session.context.settings.get_value("endorser.auto_create_rev_reg") + meta_data["processing"]["auto_create_rev_reg"] = context.settings.get_value( + "endorser.auto_create_rev_reg" + ) - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( messages_attach=cred_def["signed_txn"], @@ -429,6 +428,8 @@ async def on_cred_def_event(profile: Profile, event: Event): schema_id = event.payload["context"]["schema_id"] cred_def_id = event.payload["context"]["cred_def_id"] issuer_did = event.payload["context"]["issuer_did"] + + # after the ledger record is written, write the wallet non-secrets record await add_cred_def_non_secrets_record(profile, schema_id, issuer_did, cred_def_id) # check if we need to kick off the revocation registry setup @@ -448,6 +449,13 @@ async def on_cred_def_event(profile: Profile, event: Event): else None ) if support_revocation and novel and auto_create_rev_reg: + # this kicks off the revocation registry creation process, which is 3 steps: + # 1 - create revocation registry (ledger transaction may require endorsement) + # 2 - create revocation entry (ledger transaction may require endorsement) + # 3 - upload tails file + # For a cred def we also automatically create a second "pending" revocation + # registry, so when the first one fills up we can continue to issue credentials + # without a delay await notify_revocation_reg_event( profile, cred_def_id, diff --git a/aries_cloudagent/messaging/schemas/routes.py b/aries_cloudagent/messaging/schemas/routes.py index 98df2fa6e4..9e18970b28 100644 --- a/aries_cloudagent/messaging/schemas/routes.py +++ b/aries_cloudagent/messaging/schemas/routes.py @@ -255,9 +255,7 @@ async def schemas_send_schema(request: web.BaseRequest): return web.json_response({"schema_id": schema_id, "schema": schema_def}) else: - session = await context.session() - - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( messages_attach=schema_def["signed_txn"], @@ -404,6 +402,8 @@ def register_events(event_bus: EventBus): async def on_schema_event(profile: Profile, event: Event): """Handle any events we need to support.""" schema_id = event.payload["context"]["schema_id"] + + # after the ledger record is written, write the wallet non-secrets record await add_schema_non_secrets_record(profile, schema_id) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py index a77e202bfa..a8cff20d62 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/endorsed_transaction_response_handler.py @@ -32,8 +32,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + # profile_session = await context.session() + mgr = TransactionManager(context.profile) try: transaction = await mgr.receive_endorse_response(context.message) except TransactionManagerError: diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/refused_transaction_response_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/refused_transaction_response_handler.py index 24831ab2fe..78f3664229 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/refused_transaction_response_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/refused_transaction_response_handler.py @@ -31,8 +31,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: await mgr.receive_refuse_response(context.message) except TransactionManagerError: diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py index 5c999661ea..1142d909b9 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py @@ -31,8 +31,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: await mgr.receive_transaction_acknowledgement( context.message, context.connection_record.connection_id diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_cancel_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_cancel_handler.py index a9fcd88ffa..178fc0527b 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_cancel_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_cancel_handler.py @@ -29,8 +29,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: await mgr.receive_cancel_transaction( context.message, context.connection_record.connection_id diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py index 869338f00d..4e42cb63a8 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_job_to_send_handler.py @@ -29,8 +29,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: await mgr.set_transaction_their_job( context.message, context.message_receipt diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_request_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_request_handler.py index 90f1fa925f..d349031cba 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_request_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_request_handler.py @@ -32,8 +32,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: transaction = await mgr.receive_request( context.message, context.connection_record.connection_id diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_resend_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_resend_handler.py index 327943dfb1..3ccab43e89 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_resend_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_resend_handler.py @@ -29,8 +29,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder): if not context.connection_ready: raise HandlerException("No connection established") - profile_session = await context.session() - mgr = TransactionManager(profile_session) + mgr = TransactionManager(context.profile) try: await mgr.receive_transaction_resend( context.message, context.connection_record.connection_id diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index 31d00f062c..dca138e444 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -8,7 +8,7 @@ from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError -from ....core.profile import ProfileSession +from ....core.profile import Profile from ....indy.issuer import IndyIssuerError from ....ledger.base import BaseLedger from ....ledger.error import LedgerError @@ -40,27 +40,26 @@ class TransactionManagerError(BaseError): class TransactionManager: """Class for managing transactions.""" - def __init__(self, session: ProfileSession): + def __init__(self, profile: Profile): """ Initialize a TransactionManager. Args: session: The Profile Session for this transaction manager """ - self._session = session - self._profile = session.profile + self._profile = profile self._logger = logging.getLogger(__name__) @property - def session(self) -> ProfileSession: + def profile(self) -> Profile: """ - Accessor for the current Profile Session. + Accessor for the current Profile. Returns: - The Profile Session for this transaction manager + The Profile for this transaction manager """ - return self._session + return self._profile async def create_record( self, messages_attach: str, connection_id: str, meta_data: dict = None @@ -101,8 +100,7 @@ async def create_record( transaction.state = TransactionRecord.STATE_TRANSACTION_CREATED transaction.connection_id = connection_id - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Created a Transaction Record") return transaction @@ -152,8 +150,7 @@ async def create_request( transaction.timing = timing transaction.endorser_write_txn = endorser_write_txn - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Created an endorsement request") transaction_request = TransactionRequest( @@ -196,8 +193,7 @@ async def receive_request(self, request: TransactionRequest, connection_id: str) transaction.state = TransactionRecord.STATE_REQUEST_RECEIVED transaction.endorser_write_txn = request.endorser_write_txn - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Received an endorsement request") return transaction @@ -233,13 +229,10 @@ async def create_endorse_response( transaction._type = TransactionRecord.SIGNATURE_RESPONSE transaction_json = transaction.messages_attach[0]["data"]["json"] - profile_session = await self.session - async with profile_session as session: + async with self._profile.session() as session: wallet: BaseWallet = session.inject_or(BaseWallet) - if not wallet: raise StorageError("No wallet available") - endorser_did_info = await wallet.get_public_did() if not endorser_did_info: raise StorageError( @@ -248,12 +241,13 @@ async def create_endorse_response( endorser_did = endorser_did_info.did endorser_verkey = endorser_did_info.verkey - ledger = self._session.context.inject_or(BaseLedger) - if not ledger: - reason = "No ledger available" - if not self._session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise LedgerError(reason=reason) + async with self._profile.session() as session: + ledger = session.context.inject_or(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise LedgerError(reason=reason) async with ledger: endorsed_msg = await shield(ledger.txn_endorse(transaction_json)) @@ -276,7 +270,7 @@ async def create_endorse_response( transaction.state = state - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Created an endorsed response") if transaction.endorser_write_txn: @@ -310,8 +304,7 @@ async def receive_endorse_response(self, response: EndorsedTransactionResponse): response: The Endorsed Transaction Response """ - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, response.transaction_id ) @@ -330,13 +323,13 @@ async def receive_endorse_response(self, response: EndorsedTransactionResponse): "signature" ][endorser_did] - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Received an endorsed response") # this scenario is where the author has asked the endorser to write the ledger if transaction.endorser_write_txn: connection_id = transaction.connection_id - async with profile_session.profile.session() as session: + async with self._profile.session() as session: connection_record = await ConnRecord.retrieve_by_id( session, connection_id ) @@ -362,27 +355,29 @@ async def complete_transaction(self, transaction: TransactionRecord): """ ledger_transaction = transaction.messages_attach[0]["data"]["json"] - ledger = self._session.inject(BaseLedger) - if not ledger: - reason = "No ledger available" - if not self._session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise TransactionManagerError(reason) + async with self._profile.session() as session: + ledger = self._profile.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) - async with ledger: - try: - ledger_response_json = await shield( - ledger.txn_submit(ledger_transaction, sign=False, taa_accept=False) - ) - except (IndyIssuerError, LedgerError) as err: - raise TransactionManagerError(err.roll_up) from err + async with ledger: + try: + ledger_response_json = await shield( + ledger.txn_submit( + ledger_transaction, sign=False, taa_accept=False + ) + ) + except (IndyIssuerError, LedgerError) as err: + raise TransactionManagerError(err.roll_up) from err ledger_response = json.loads(ledger_response_json) - profile_session = await self.session transaction.state = TransactionRecord.STATE_TRANSACTION_ACKED - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Completed transaction") # this scenario is where the endorser is writing the transaction @@ -391,9 +386,9 @@ async def complete_transaction(self, transaction: TransactionRecord): return ledger_response connection_id = transaction.connection_id - async with profile_session.profile.session() as session: + async with self._profile.session() as session: connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - jobs = await connection_record.metadata_get(self._session, "transaction_jobs") + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise TransactionManagerError( "The transaction related jobs are not set up in " @@ -431,8 +426,7 @@ async def receive_transaction_acknowledgement( connection_id: The connection_id related to this Transaction Record """ - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: transaction = await TransactionRecord.retrieve_by_connection_and_thread( session, connection_id, response.thread_id ) @@ -443,19 +437,19 @@ async def receive_transaction_acknowledgement( ) transaction.state = TransactionRecord.STATE_TRANSACTION_ACKED - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Received a transaction ack") connection_id = transaction.connection_id try: - async with profile_session.profile.session() as session: + async with self._profile.session() as session: connection_record = await ConnRecord.retrieve_by_id( session, connection_id ) + jobs = await connection_record.metadata_get(session, "transaction_jobs") except StorageNotFoundError as err: raise TransactionManagerError(err.roll_up) from err - jobs = await connection_record.metadata_get(self._session, "transaction_jobs") if not jobs: raise TransactionManagerError( "The transaction related jobs are not set up in " @@ -511,8 +505,7 @@ async def create_refuse_response( transaction.state = state - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Created a refused response") refused_transaction_response = RefusedTransactionResponse( @@ -533,8 +526,7 @@ async def receive_refuse_response(self, response: RefusedTransactionResponse): response: The refused transaction response """ - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, response.transaction_id ) @@ -546,7 +538,7 @@ async def receive_refuse_response(self, response: RefusedTransactionResponse): transaction.signature_response.append(response.signature_response) transaction.thread_id = response.thread_id - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Received a refused response") return transaction @@ -574,8 +566,7 @@ async def cancel_transaction(self, transaction: TransactionRecord, state: str): ) transaction.state = state - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Cancelled the transaction") cancelled_transaction_response = CancelTransaction( @@ -595,14 +586,13 @@ async def receive_cancel_transaction( connection_id: The connection_id related to this Transaction Record """ - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: transaction = await TransactionRecord.retrieve_by_connection_and_thread( session, connection_id, response.thread_id ) transaction.state = response.state - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Received a cancel request") return transaction @@ -630,8 +620,7 @@ async def transaction_resend(self, transaction: TransactionRecord, state: str): ) transaction.state = state - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Resends the transaction request") resend_transaction_response = TransactionResend( @@ -652,14 +641,13 @@ async def receive_transaction_resend( connection_id: The connection_id related to this Transaction Record """ - profile_session = await self.session - async with profile_session.profile.session() as session: + async with self._profile.session() as session: transaction = await TransactionRecord.retrieve_by_connection_and_thread( session, connection_id, response.thread_id ) transaction.state = response.state - async with profile_session.profile.session() as session: + async with self._profile.session() as session: await transaction.save(session, reason="Receives a transaction request") return transaction @@ -677,15 +665,16 @@ async def set_transaction_my_job(self, record: ConnRecord, transaction_my_job: s """ - value = await record.metadata_get(self._session, "transaction_jobs") - - if value: - value["transaction_my_job"] = transaction_my_job - else: - value = {"transaction_my_job": transaction_my_job} - await record.metadata_set(self._session, key="transaction_jobs", value=value) + async with self._profile.session() as session: + value = await record.metadata_get(session, "transaction_jobs") + if value: + value["transaction_my_job"] = transaction_my_job + else: + value = {"transaction_my_job": transaction_my_job} + await record.metadata_set(session, key="transaction_jobs", value=value) tx_job_to_send = TransactionJobToSend(job=transaction_my_job) + return tx_job_to_send async def set_transaction_their_job( @@ -700,21 +689,21 @@ async def set_transaction_their_job( """ try: - connection = await ConnRecord.retrieve_by_did( - self._session, receipt.sender_did, receipt.recipient_did - ) + async with self._profile.session() as session: + connection = await ConnRecord.retrieve_by_did( + session, receipt.sender_did, receipt.recipient_did + ) + value = await connection.metadata_get(session, "transaction_jobs") + if value: + value["transaction_their_job"] = tx_job_received.job + else: + value = {"transaction_their_job": tx_job_received.job} + await connection.metadata_set( + session, key="transaction_jobs", value=value + ) except StorageNotFoundError as err: raise TransactionManagerError(err.roll_up) from err - value = await connection.metadata_get(self._session, "transaction_jobs") - if value: - value["transaction_their_job"] = tx_job_received.job - else: - value = {"transaction_their_job": tx_job_received.job} - await connection.metadata_set( - self._session, key="transaction_jobs", value=value - ) - async def endorsed_txn_post_processing( self, transaction: TransactionRecord, @@ -722,19 +711,20 @@ async def endorsed_txn_post_processing( connection_record: ConnRecord = None, ): """ - Store record in wallet. + Store record in wallet, and kick off any required post-processing. Args: transaction: The transaction from which the schema/cred_def would be stored in wallet. """ - ledger = self._session.inject(BaseLedger) - if not ledger: - reason = "No ledger available" - if not self._session.context.settings.get_value("wallet.type"): - reason += ": missing wallet-type?" - raise TransactionManagerError(reason) + async with self._profile.session() as session: + ledger = self._profile.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) # setup meta_data to pass to future events, if necessary meta_data = transaction.meta_data @@ -780,21 +770,21 @@ async def endorsed_txn_post_processing( "auto_create_rev_reg", False ) - # Notify event + # 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 registry transaction + # 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 ) - # Notify event + # If "auto_processing" is enabled, also upload tails file for this registry if auto_create_rev_reg: await notify_revocation_tails_file_event( self._profile, rev_reg_id, meta_data diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index a151e5fe3f..ee55492e71 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -1,7 +1,6 @@ """Endorse Transaction handling admin routes.""" import json -from typing import Optional from aiohttp import web from aiohttp_apispec import ( @@ -21,7 +20,6 @@ from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import UUIDFour from ....storage.error import StorageError, StorageNotFoundError -from ....wallet.base import BaseWallet from .manager import TransactionManager, TransactionManagerError from .models.transaction_record import TransactionRecord, TransactionRecordSchema @@ -141,7 +139,7 @@ async def transactions_list(request: web.BaseRequest): post_filter = {} try: - async with context.session() as session: + async with context.profile.session() as session: records = await TransactionRecord.query( session, tag_filter, post_filter_positive=post_filter, alt=True ) @@ -169,7 +167,7 @@ async def transactions_retrieve(request: web.BaseRequest): transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: record = await TransactionRecord.retrieve_by_id(session, transaction_id) result = record.serialize() except StorageNotFoundError as err: @@ -209,7 +207,7 @@ async def transaction_create_request(request: web.BaseRequest): expires_time = body.get("expires_time") try: - async with context.session() as session: + async with context.profile.session() as session: transaction_record = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -221,8 +219,8 @@ async def transaction_create_request(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -252,7 +250,7 @@ async def transaction_create_request(request: web.BaseRequest): reason="A request can only be created to a TRANSACTION_ENDORSER" ) - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: transaction_record, transaction_request = await transaction_mgr.create_request( transaction=transaction_record, @@ -292,7 +290,7 @@ async def endorse_transaction_response(request: web.BaseRequest): transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -305,8 +303,8 @@ async def endorse_transaction_response(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -319,7 +317,7 @@ async def endorse_transaction_response(request: web.BaseRequest): reason="Only a TRANSACTION_ENDORSER can endorse a transaction" ) - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: ( transaction, @@ -358,22 +356,10 @@ async def refuse_transaction_response(request: web.BaseRequest): context: AdminRequestContext = request["context"] outbound_handler = request["outbound_message_router"] - session = await context.session() - - wallet: Optional[BaseWallet] = session.inject_or(BaseWallet) - - if not wallet: - raise web.HTTPForbidden(reason="No wallet available") - refuser_did_info = await wallet.get_public_did() - if not refuser_did_info: - raise web.HTTPForbidden( - reason="Transaction cannot be refused as there is no Public DID in wallet" - ) - refuser_did = refuser_did_info.did transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -385,8 +371,8 @@ async def refuse_transaction_response(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -400,14 +386,14 @@ async def refuse_transaction_response(request: web.BaseRequest): ) try: - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) ( transaction, refused_transaction_response, ) = await transaction_mgr.create_refuse_response( transaction=transaction, state=TransactionRecord.STATE_TRANSACTION_REFUSED, - refuser_did=refuser_did, + refuser_did=None, ) except (StorageError, TransactionManagerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -440,7 +426,7 @@ async def cancel_transaction(request: web.BaseRequest): transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -452,8 +438,8 @@ async def cancel_transaction(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -466,7 +452,7 @@ async def cancel_transaction(request: web.BaseRequest): reason="Only a TRANSACTION_AUTHOR can cancel a transaction" ) - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: ( transaction, @@ -505,7 +491,7 @@ async def transaction_resend(request: web.BaseRequest): transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -517,8 +503,8 @@ async def transaction_resend(request: web.BaseRequest): except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await connection_record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -532,7 +518,7 @@ async def transaction_resend(request: web.BaseRequest): ) try: - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) ( transaction, resend_transaction_response, @@ -570,20 +556,21 @@ async def set_endorser_role(request: web.BaseRequest): outbound_handler = request["outbound_message_router"] connection_id = request.match_info["conn_id"] transaction_my_job = request.query.get("transaction_my_job") - session = await context.session() - try: - 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 + async with context.profile.session() as session: + try: + 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 - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) tx_job_to_send = await transaction_mgr.set_transaction_my_job( record=record, transaction_my_job=transaction_my_job ) - jobs = await record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + jobs = await record.metadata_get(session, "transaction_jobs") await outbound_handler(tx_job_to_send, connection_id=connection_id) return web.json_response(jobs) @@ -610,15 +597,15 @@ async def set_endorser_info(request: web.BaseRequest): connection_id = request.match_info["conn_id"] endorser_did = request.query.get("endorser_did") endorser_name = request.query.get("endorser_name") - session = await context.session() - try: - 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 - jobs = await record.metadata_get(session, "transaction_jobs") + async with context.profile.session() as session: + try: + 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 + jobs = await record.metadata_get(session, "transaction_jobs") if not jobs: raise web.HTTPForbidden( reason=( @@ -640,15 +627,16 @@ async def set_endorser_info(request: web.BaseRequest): "to metadata of its connection record" ) ) - value = await record.metadata_get(session, "endorser_info") - if value: - value["endorser_did"] = endorser_did - value["endorser_name"] = endorser_name - else: - value = {"endorser_did": endorser_did, "endorser_name": endorser_name} - await record.metadata_set(session, key="endorser_info", value=value) + async with context.profile.session() as session: + value = await record.metadata_get(session, "endorser_info") + if value: + value["endorser_did"] = endorser_did + value["endorser_name"] = endorser_name + else: + value = {"endorser_did": endorser_did, "endorser_name": endorser_name} + await record.metadata_set(session, key="endorser_info", value=value) - endorser_info = await record.metadata_get(session, "endorser_info") + endorser_info = await record.metadata_get(session, "endorser_info") return web.json_response(endorser_info) @@ -674,7 +662,7 @@ async def transaction_write(request: web.BaseRequest): transaction_id = request.match_info["tran_id"] try: - async with context.session() as session: + async with context.profile.session() as session: transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) @@ -690,8 +678,7 @@ async def transaction_write(request: web.BaseRequest): ) # update the final transaction status - session = await context.session() - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: ( tx_completed, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index de9c007cd3..7ef3cc6ef2 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -5,10 +5,12 @@ from asynctest import mock as async_mock from asynctest import TestCase as AsyncTestCase +from .....admin.request_context import AdminRequestContext from .....cache.base import BaseCache from .....cache.in_memory import InMemoryCache from .....connections.models.conn_record import ConnRecord from .....core.in_memory import InMemoryProfile +from .....core.profile import Profile from .....ledger.base import BaseLedger from .....storage.error import StorageNotFoundError from .....wallet.base import BaseWallet @@ -33,24 +35,6 @@ class TestTransactionManager(AsyncTestCase): async def setUp(self): - self.wallet = async_mock.MagicMock( - get_public_did=async_mock.CoroutineMock( - return_value=DIDInfo( - "DJGEjaMunDtFtBVrn1qJMT", - "verkey", - {"meta": "data"}, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, - ) - ) - ) - self.session = InMemoryProfile.test_session(bind={BaseWallet: self.wallet}) - self.profile = self.session.profile - self.context = self.profile.context - setattr( - self.profile, "session", async_mock.MagicMock(return_value=self.session) - ) - sigs = [ ( "2iNTeFy44WK9zpsPfcwfu489aHWroYh3v8mme9tPyNKn" @@ -130,11 +114,25 @@ async def setUp(self): self.ledger.txn_endorse = async_mock.CoroutineMock( return_value=self.test_endorsed_message ) - self.session.context.injector.bind_instance(BaseLedger, self.ledger) - self.manager = TransactionManager(self.session) + self.context = AdminRequestContext.test_context() + self.profile = self.context.profile + injector = self.profile.context.injector + injector.bind_instance(BaseLedger, self.ledger) + + async with self.profile.session() as session: + self.wallet: BaseWallet = session.inject_or(BaseWallet) + await self.wallet.create_local_did( + DIDMethod.SOV, + KeyType.ED25519, + did="DJGEjaMunDtFtBVrn1qJMT", + metadata={"meta": "data"}, + ) + await self.wallet.set_public_did("DJGEjaMunDtFtBVrn1qJMT") + + self.manager = TransactionManager(self.profile) - assert self.manager.session + assert self.manager.profile async def test_transaction_jobs(self): author = TransactionJob.TRANSACTION_AUTHOR @@ -168,13 +166,13 @@ async def test_create_record(self): ) async def test_txn_rec_retrieve_by_connection_and_thread_caching(self): - async with self.session.profile.session() as sesn: + async with self.profile.session() as sesn: sesn.context.injector.bind_instance(BaseCache, InMemoryCache()) txn_rec = TransactionRecord( connection_id="123", thread_id="456", ) - await txn_rec.save(self.session) + await txn_rec.save(sesn) await TransactionRecord.retrieve_by_connection_and_thread( session=sesn, connection_id="123", diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 52c3d043a3..3855255fb7 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -27,6 +27,12 @@ def setUp(self): self.session_inject = {} self.profile = InMemoryProfile.test_profile() self.profile_injector = self.profile.context.injector + self.profile_session = InMemoryProfile.test_session() + setattr( + self.profile, + "session", + async_mock.MagicMock(return_value=self.profile_session), + ) self.ledger = async_mock.create_autospec(BaseLedger) self.ledger.__aenter__ = async_mock.CoroutineMock(return_value=self.ledger) @@ -794,20 +800,6 @@ async def test_refuse_transaction_response(self): mock_response.assert_called_once_with({"...": "..."}) - async def test_refuse_transaction_response_no_wallet_x(self): - self.session_inject[BaseWallet] = None - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.refuse_transaction_response(self.request) - - async def test_refuse_transaction_response_no_endorser_did_info_x(self): - self.request.match_info = {"tran_id": "dummy"} - self.session_inject[BaseWallet] = async_mock.MagicMock( - get_public_did=async_mock.CoroutineMock(return_value=None) - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.refuse_transaction_response(self.request) - async def test_refuse_transaction_response_not_found_x(self): self.request.match_info = {"tran_id": "dummy"} @@ -929,15 +921,18 @@ async def test_refuse_transaction_response_txn_mgr_x(self): self.request.match_info = {"tran_id": "dummy"} self.session_inject[BaseWallet] = async_mock.MagicMock( - get_public_did=async_mock.CoroutineMock( - return_value=DIDInfo( - "did", - "verkey", - {"meta": "data"}, - method=DIDMethod.SOV, - key_type=KeyType.ED25519, + BaseWallet, + async_mock.MagicMock( + get_public_did=async_mock.CoroutineMock( + return_value=DIDInfo( + "did", + "verkey", + {"meta": "data"}, + method=DIDMethod.SOV, + key_type=KeyType.ED25519, + ) ) - ) + ), ) with async_mock.patch.object( diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index e5bef0df19..ba403dda20 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -135,32 +135,33 @@ async def publish_pending_revocations( result = {} issuer = self._profile.inject(IndyIssuer) - txn = await self._profile.transaction() - issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(txn) - for issuer_rr_rec in issuer_rr_recs: - rrid = issuer_rr_rec.revoc_reg_id - crids = [] - if not rrid2crid: - crids = issuer_rr_rec.pending_pub - elif rrid in rrid2crid: - crids = [ - crid - for crid in issuer_rr_rec.pending_pub - if crid in (rrid2crid[rrid] or []) or not rrid2crid[rrid] - ] - if crids: - # FIXME - must use the same transaction - (delta_json, failed_crids) = await issuer.revoke_credentials( - issuer_rr_rec.revoc_reg_id, - issuer_rr_rec.tails_local_path, - crids, - ) - issuer_rr_rec.revoc_reg_entry = json.loads(delta_json) - await issuer_rr_rec.send_entry(self._profile) - published = [crid for crid in crids if crid not in failed_crids] - result[issuer_rr_rec.revoc_reg_id] = published - await issuer_rr_rec.clear_pending(txn, published) - await txn.commit() + async with self._profile.transaction() as txn: + issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(txn) + for issuer_rr_rec in issuer_rr_recs: + rrid = issuer_rr_rec.revoc_reg_id + crids = [] + if not rrid2crid: + crids = issuer_rr_rec.pending_pub + elif rrid in rrid2crid: + crids = [ + crid + for crid in issuer_rr_rec.pending_pub + if crid in (rrid2crid[rrid] or []) or not rrid2crid[rrid] + ] + if crids: + # FIXME - must use the same transaction + (delta_json, failed_crids) = await issuer.revoke_credentials( + issuer_rr_rec.revoc_reg_id, + issuer_rr_rec.tails_local_path, + crids, + transaction=txn, + ) + issuer_rr_rec.revoc_reg_entry = json.loads(delta_json) + await issuer_rr_rec.send_entry(self._profile) + published = [crid for crid in crids if crid not in failed_crids] + result[issuer_rr_rec.revoc_reg_id] = published + await issuer_rr_rec.clear_pending(txn, published) + await txn.commit() return result diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 1b72b7f7e3..57b77159e3 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -325,13 +325,13 @@ class RevRegConnIdMatchInfoSchema(OpenAPISchema): @response_schema(RevocationModuleResponseSchema(), description="") async def revoke(request: web.BaseRequest): """ - Request handler for storing a credential request. + Request handler for storing a credential revocation. Args: request: aiohttp request object Returns: - The credential request details. + The credential revocation details. """ context: AdminRequestContext = request["context"] @@ -782,9 +782,7 @@ async def send_rev_reg_def(request: web.BaseRequest): return web.json_response({"result": rev_reg.serialize()}) else: - session = await context.session() - - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( messages_attach=rev_reg_resp["result"], connection_id=connection_id @@ -894,9 +892,7 @@ async def send_rev_reg_entry(request: web.BaseRequest): return web.json_response({"result": rev_reg.serialize()}) else: - session = await context.session() - - transaction_mgr = TransactionManager(session) + transaction_mgr = TransactionManager(context.profile) try: transaction = await transaction_mgr.create_record( messages_attach=rev_entry_resp["result"], connection_id=connection_id @@ -1001,10 +997,13 @@ async def on_revocation_event(profile: Profile, event: Event): """Handle any events we need to support.""" event_topic_parts = event.topic.split("::") if event_topic_parts[2] == REVOCATION_REG_EVENT: + # create the revocation registry (ledger transaction may require endorsement) await on_revocation_registry_event(profile, event) elif event_topic_parts[2] == REVOCATION_ENTRY_EVENT: + # create the revocation entry (ledger transaction may require endorsement) await on_revocation_entry_event(profile, event) elif event_topic_parts[2] == REVOCATION_TAILS_EVENT: + # upload tails file await on_revocation_tails_file_event(profile, event) else: # TODO error handling @@ -1070,32 +1069,32 @@ async def on_revocation_registry_event(profile: Profile, event: Event): await notify_revocation_entry_event(profile, rev_reg_id, meta_data) else: - async with profile.session() as session: - transaction_manager = TransactionManager(session) + transaction_manager = TransactionManager(profile) + try: + revo_transaction = await transaction_manager.create_record( + messages_attach=rev_reg_resp["result"], + connection_id=connection.connection_id, + meta_data=event.payload, + ) + except StorageError as err: + raise TransactionManagerError(reason=err.roll_up) from err + + # if auto-request, send the request to the endorser + if profile.settings.get_value("endorser.auto_request"): try: - revo_transaction = await transaction_manager.create_record( - messages_attach=rev_reg_resp["result"], - connection_id=connection.connection_id, - meta_data=event.payload, + ( + revo_transaction, + revo_transaction_request, + ) = await transaction_manager.create_request( + transaction=revo_transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, ) - except StorageError as err: + except (StorageError, TransactionManagerError) as err: raise TransactionManagerError(reason=err.roll_up) from err - # if auto-request, send the request to the endorser - if profile.settings.get_value("endorser.auto_request"): - try: - ( - revo_transaction, - revo_transaction_request, - ) = await transaction_manager.create_request( - transaction=revo_transaction, - # TODO see if we need to parameterize these params - # expires_time=expires_time, - # endorser_write_txn=endorser_write_txn, - ) - except (StorageError, TransactionManagerError) as err: - raise TransactionManagerError(reason=err.roll_up) from err - + async with profile.session() as session: responder = session.inject_or(BaseResponder) if responder: await responder.send( @@ -1154,32 +1153,32 @@ async def on_revocation_entry_event(profile: Profile, event: Event): await notify_revocation_tails_file_event(profile, rev_reg_id, meta_data) else: - async with profile.session() as session: - transaction_manager = TransactionManager(session) + transaction_manager = TransactionManager(profile) + try: + revo_transaction = await transaction_manager.create_record( + messages_attach=rev_entry_resp["result"], + connection_id=connection.connection_id, + meta_data=event.payload, + ) + except StorageError as err: + raise RevocationError(reason=err.roll_up) from err + + # if auto-request, send the request to the endorser + if profile.settings.get_value("endorser.auto_request"): try: - revo_transaction = await transaction_manager.create_record( - messages_attach=rev_entry_resp["result"], - connection_id=connection.connection_id, - meta_data=event.payload, + ( + revo_transaction, + revo_transaction_request, + ) = await transaction_manager.create_request( + transaction=revo_transaction, + # TODO see if we need to parameterize these params + # expires_time=expires_time, + # endorser_write_txn=endorser_write_txn, ) - except StorageError as err: + except (StorageError, TransactionManagerError) as err: raise RevocationError(reason=err.roll_up) from err - # if auto-request, send the request to the endorser - if profile.settings.get_value("endorser.auto_request"): - try: - ( - revo_transaction, - revo_transaction_request, - ) = await transaction_manager.create_request( - transaction=revo_transaction, - # TODO see if we need to parameterize these params - # expires_time=expires_time, - # endorser_write_txn=endorser_write_txn, - ) - except (StorageError, TransactionManagerError) as err: - raise RevocationError(reason=err.roll_up) from err - + async with profile.session() as session: responder = session.inject_or(BaseResponder) if responder: await responder.send( @@ -1219,6 +1218,9 @@ async def on_revocation_tails_file_event(profile: Profile, event: Event): ) # 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 + # delay) create_pending_rev_reg = event.payload["processing"].get( "create_pending_rev_reg", False ) diff --git a/demo/INTEGRATION-TESTS.md b/demo/INTEGRATION-TESTS.md index 0007633149..2d4726398a 100644 --- a/demo/INTEGRATION-TESTS.md +++ b/demo/INTEGRATION-TESTS.md @@ -30,6 +30,14 @@ docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432 ACAPY_ARG_FILE=postgres-indy-args.yml ./run_bdd ``` +To run the tests against the back-end `askar` libraries (as opposed to indy-sdk) run the following: + +```bash +BDD_EXTRA_AGENT_ARGS="{\"wallet-type\":\"askar\"}" ./run_bdd +``` + +(Note that `wallet-type` is currently the only extra argument supported.) + You can run individual tests by specifying the tag(s): ```bash diff --git a/demo/README.md b/demo/README.md index 71c2a4d809..43df1767f4 100644 --- a/demo/README.md +++ b/demo/README.md @@ -24,6 +24,7 @@ There are several demos available for ACA-Py mostly (but not only) aimed at deve - [Multi-tenancy](#multi-tenancy) - [DID Exchange](#did-exchange) - [Endorser](#endorser) + - [Run Askar Backend](#run-askar-backend) - [Learning about the Alice/Faber code](#learning-about-the-alicefaber-code) - [OpenAPI (Swagger) Demo](#openapi-swagger-demo) - [Performance Demo](#performance-demo) @@ -245,6 +246,14 @@ Note that you can't (currently) use the DID Exchange protocol to connect with an This is described in [Endorser.md](Endorser.md) +### Run Askar Backend + +This runs using the askar libraries instead of indy-sdk: + +```bash +./run_demo faber --wallet-type askar +``` + ### Mediation To enable mediation, run the `alice` or `faber` demo with the `--mediation` option: diff --git a/demo/features/steps/0160-connection.py b/demo/features/steps/0160-connection.py index eb8954d5b2..479611964d 100644 --- a/demo/features/steps/0160-connection.py +++ b/demo/features/steps/0160-connection.py @@ -10,6 +10,7 @@ from behave import given, when, then import json +import os from bdd_support.agent_backchannel_client import ( create_agent_container_with_args, @@ -23,6 +24,9 @@ from runners.agent_container import AgentContainer +BDD_EXTRA_AGENT_ARGS = os.getenv("BDD_EXTRA_AGENT_ARGS") + + @given("{n} agents") @given(u"we have {n} agents") def step_impl(context, n): @@ -30,6 +34,11 @@ def step_impl(context, n): start_port = 8020 + extra_args = None + if BDD_EXTRA_AGENT_ARGS: + print("Got extra args:", BDD_EXTRA_AGENT_ARGS) + extra_args = json.loads(BDD_EXTRA_AGENT_ARGS) + context.active_agents = {} for row in context.table: agent_name = row["name"] @@ -43,6 +52,13 @@ def step_impl(context, n): ] if agent_params and 0 < len(agent_params): in_args.extend(agent_params.split(" ")) + if extra_args and extra_args.get("wallet-type"): + in_args.extend( + [ + "--wallet-type", + extra_args.get("wallet-type"), + ] + ) context.active_agents[agent_name] = { "name": agent_name, diff --git a/demo/run_bdd b/demo/run_bdd index 56f7d8ae0b..da898a79b2 100755 --- a/demo/run_bdd +++ b/demo/run_bdd @@ -216,6 +216,9 @@ fi if ! [ -z "$ACAPY_ARG_FILE" ]; then DOCKER_ENV="${DOCKER_ENV} -e ACAPY_ARG_FILE=${ACAPY_ARG_FILE}" fi +if ! [ -z "$BDD_EXTRA_AGENT_ARGS" ]; then + DOCKER_ENV="${DOCKER_ENV} -e BDD_EXTRA_AGENT_ARGS=${BDD_EXTRA_AGENT_ARGS}" +fi if ! [ -z "${ENABLE_PYDEVD_PYCHARM}" ]; then DOCKER_ENV="${DOCKER_ENV} -e ENABLE_PYDEVD_PYCHARM=${ENABLE_PYDEVD_PYCHARM} -e PYDEVD_PYCHARM_CONTROLLER_PORT=${PYDEVD_PYCHARM_CONTROLLER_PORT} -e PYDEVD_PYCHARM_AGENT_PORT=${PYDEVD_PYCHARM_AGENT_PORT}"