diff --git a/aries_cloudagent/indy/credx/issuer.py b/aries_cloudagent/indy/credx/issuer.py index b0108fedbc..6547842978 100644 --- a/aries_cloudagent/indy/credx/issuer.py +++ b/aries_cloudagent/indy/credx/issuer.py @@ -28,8 +28,6 @@ DEFAULT_CRED_DEF_TAG, DEFAULT_SIGNATURE_TYPE, ) -from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord - LOGGER = logging.getLogger(__name__) @@ -225,7 +223,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, revoc_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -237,7 +234,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec revoc_reg_id: ID of the revocation registry tails_file_path: The location of the tails file @@ -324,20 +320,6 @@ async def create_credential( await txn.handle.replace( CATEGORY_REV_REG_INFO, revoc_reg_id, value_json=rev_info ) - - issuer_cr_rec = IssuerCredRevRecord( - state=IssuerCredRevRecord.STATE_ISSUED, - cred_ex_id=cred_ex_id, - rev_reg_id=revoc_reg_id, - cred_rev_id=str(rev_reg_index), - ) - await issuer_cr_rec.save( - txn, - reason=( - "Created issuer cred rev record for " - f"rev reg id {revoc_reg_id}, {rev_reg_index}" - ), - ) await txn.commit() except AskarError as err: raise IndyIssuerError( diff --git a/aries_cloudagent/indy/credx/tests/test_cred_issuance.py b/aries_cloudagent/indy/credx/tests/test_cred_issuance.py index 0c23eda904..027c16b94d 100644 --- a/aries_cloudagent/indy/credx/tests/test_cred_issuance.py +++ b/aries_cloudagent/indy/credx/tests/test_cred_issuance.py @@ -137,7 +137,6 @@ async def test_issue_store_non_rev(self): cred_offer, cred_req, {"name": "NAME", "moniker": "MONIKER"}, - cred_ex_id="cred_ex_id", revoc_reg_id=None, tails_file_path=None, ) @@ -255,7 +254,6 @@ async def test_issue_store_rev(self): cred_offer, cred_req, {"name": "NAME", "moniker": "MONIKER"}, - cred_ex_id="cred_ex_id", revoc_reg_id=reg_id, tails_file_path=tails_path, ) diff --git a/aries_cloudagent/indy/issuer.py b/aries_cloudagent/indy/issuer.py index 3503626f72..d889746d41 100644 --- a/aries_cloudagent/indy/issuer.py +++ b/aries_cloudagent/indy/issuer.py @@ -122,7 +122,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, revoc_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -134,7 +133,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec revoc_reg_id: ID of the revocation registry tails_file_path: The location of the tails file diff --git a/aries_cloudagent/indy/sdk/issuer.py b/aries_cloudagent/indy/sdk/issuer.py index 72f569f2ee..3d84473c93 100644 --- a/aries_cloudagent/indy/sdk/issuer.py +++ b/aries_cloudagent/indy/sdk/issuer.py @@ -10,7 +10,6 @@ from ...indy.sdk.profile import IndySdkProfile from ...messaging.util import encode -from ...revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ...storage.error import StorageError from ..issuer import ( @@ -162,7 +161,6 @@ async def create_credential( credential_offer: dict, credential_request: dict, credential_values: dict, - cred_ex_id: str, rev_reg_id: str = None, tails_file_path: str = None, ) -> Tuple[str, str]: @@ -174,7 +172,6 @@ async def create_credential( credential_offer: Credential Offer to create credential for credential_request: Credential request to create credential for credential_values: Values to go in credential - cred_ex_id: credential exchange identifier to use in issuer cred rev rec rev_reg_id: ID of the revocation registry tails_file_path: Path to the local tails file @@ -219,22 +216,6 @@ async def create_credential( rev_reg_id, tails_reader_handle, ) - - if cred_rev_id: - issuer_cr_rec = IssuerCredRevRecord( - state=IssuerCredRevRecord.STATE_ISSUED, - cred_ex_id=cred_ex_id, - rev_reg_id=rev_reg_id, - cred_rev_id=cred_rev_id, - ) - async with self.profile.session() as session: - await issuer_cr_rec.save( - session, - reason=( - "Created issuer cred rev record for " - f"rev reg id {rev_reg_id}, {cred_rev_id}" - ), - ) except AnoncredsRevocationRegistryFullError: LOGGER.warning( "Revocation registry %s is full: cannot create credential", diff --git a/aries_cloudagent/indy/sdk/tests/test_issuer.py b/aries_cloudagent/indy/sdk/tests/test_issuer.py index 0378044314..c82a55a771 100644 --- a/aries_cloudagent/indy/sdk/tests/test_issuer.py +++ b/aries_cloudagent/indy/sdk/tests/test_issuer.py @@ -166,58 +166,46 @@ async def test_create_revoke_credentials( for cr_id in test_cred_rev_ids ] - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) - ) - - with self.assertRaises(test_module.IndyIssuerError): # missing attribute - cred_json, revoc_id = await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - {}, - "dummy-cxid", - ) - - (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + with self.assertRaises(test_module.IndyIssuerError): # missing attribute + cred_json, revoc_id = await self.issuer.create_credential( test_schema, test_offer, test_request, - test_values, - "dummy-cxid", - REV_REG_ID, - "/tmp/tails/path/dummy", - ) - mock_indy_create_credential.assert_called_once() - ( - call_wallet, - call_offer, - call_request, - call_values, - call_etc1, - call_etc2, - ) = mock_indy_create_credential.call_args[0] - assert call_wallet is self.wallet.handle - assert json.loads(call_offer) == test_offer - assert json.loads(call_request) == test_request - values = json.loads(call_values) - assert "attr1" in values - - mock_indy_revoke_credential.return_value = json.dumps(TEST_RR_DELTA) - mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) - (result, failed) = await self.issuer.revoke_credentials( - REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + {}, ) - assert json.loads(result) == TEST_RR_DELTA - assert not failed - assert mock_indy_revoke_credential.call_count == 2 - mock_indy_merge_rr_deltas.assert_called_once() + + (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + test_schema, + test_offer, + test_request, + test_values, + REV_REG_ID, + "/tmp/tails/path/dummy", + ) + mock_indy_create_credential.assert_called_once() + ( + call_wallet, + call_offer, + call_request, + call_values, + call_etc1, + call_etc2, + ) = mock_indy_create_credential.call_args[0] + assert call_wallet is self.wallet.handle + assert json.loads(call_offer) == test_offer + assert json.loads(call_request) == test_request + values = json.loads(call_values) + assert "attr1" in values + + mock_indy_revoke_credential.return_value = json.dumps(TEST_RR_DELTA) + mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) + (result, failed) = await self.issuer.revoke_credentials( + REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + ) + assert json.loads(result) == TEST_RR_DELTA + assert not failed + assert mock_indy_revoke_credential.call_count == 2 + mock_indy_merge_rr_deltas.assert_called_once() @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) @@ -266,73 +254,53 @@ async def test_create_revoke_credentials_x( test_offer, test_request, {}, - "dummy-cxid", ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock( - side_effect=test_module.StorageError( - "could not store" # not fatal; maximize coverage - ) - ) - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock( - side_effect=test_module.StorageError( - "could not store" # not fatal; maximize coverage - ) - ), - ) - ) - - (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - REV_REG_ID, - "/tmp/tails/path/dummy", - ) - mock_indy_create_credential.assert_called_once() - ( - call_wallet, - call_offer, - call_request, - call_values, - call_etc1, - call_etc2, - ) = mock_indy_create_credential.call_args[0] - assert call_wallet is self.wallet.handle - assert json.loads(call_offer) == test_offer - assert json.loads(call_request) == test_request - values = json.loads(call_values) - assert "attr1" in values - - def mock_revoke(_h, _t, _r, cred_rev_id): - if cred_rev_id == "42": - return json.dumps(TEST_RR_DELTA) - if cred_rev_id == "54": - raise IndyError( - error_code=ErrorCode.AnoncredsInvalidUserRevocId, - error_details={"message": "already revoked"}, - ) + (cred_json, cred_rev_id) = await self.issuer.create_credential( # main line + test_schema, + test_offer, + test_request, + test_values, + REV_REG_ID, + "/tmp/tails/path/dummy", + ) + mock_indy_create_credential.assert_called_once() + ( + call_wallet, + call_offer, + call_request, + call_values, + call_etc1, + call_etc2, + ) = mock_indy_create_credential.call_args[0] + assert call_wallet is self.wallet.handle + assert json.loads(call_offer) == test_offer + assert json.loads(call_request) == test_request + values = json.loads(call_values) + assert "attr1" in values + + def mock_revoke(_h, _t, _r, cred_rev_id): + if cred_rev_id == "42": + return json.dumps(TEST_RR_DELTA) + if cred_rev_id == "54": raise IndyError( - error_code=ErrorCode.UnknownCryptoTypeError, - error_details={"message": "truly an outlier"}, + error_code=ErrorCode.AnoncredsInvalidUserRevocId, + error_details={"message": "already revoked"}, ) - - mock_indy_revoke_credential.side_effect = mock_revoke - mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) - (result, failed) = await self.issuer.revoke_credentials( - REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + raise IndyError( + error_code=ErrorCode.UnknownCryptoTypeError, + error_details={"message": "truly an outlier"}, ) - assert json.loads(result) == TEST_RR_DELTA - assert failed == ["54", "103"] - assert mock_indy_revoke_credential.call_count == 3 - mock_indy_merge_rr_deltas.assert_not_called() + + mock_indy_revoke_credential.side_effect = mock_revoke + mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA) + (result, failed) = await self.issuer.revoke_credentials( + REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids + ) + assert json.loads(result) == TEST_RR_DELTA + assert failed == ["54", "103"] + assert mock_indy_revoke_credential.call_count == 3 + mock_indy_merge_rr_deltas.assert_not_called() @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) @@ -357,25 +325,14 @@ async def test_create_credential_rr_full( error_code=ErrorCode.AnoncredsRevocationRegistryFullError ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) + with self.assertRaises(IndyIssuerRevocationRegistryFullError): + await self.issuer.create_credential( + test_schema, + test_offer, + test_request, + test_values, ) - with self.assertRaises(IndyIssuerRevocationRegistryFullError): - await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - ) - @async_mock.patch("indy.anoncreds.issuer_create_credential") @async_mock.patch.object(test_module, "create_tails_reader", autospec=True) async def test_create_credential_x_indy( @@ -400,25 +357,14 @@ async def test_create_credential_x_indy( error_code=ErrorCode.WalletInvalidHandle ) - with async_mock.patch.object( - test_module, "IssuerCredRevRecord", async_mock.MagicMock() - ) as mock_issuer_cr_rec: - mock_issuer_cr_rec.return_value.save = async_mock.CoroutineMock() - mock_issuer_cr_rec.retrieve_by_ids = async_mock.CoroutineMock( - return_value=async_mock.MagicMock( - set_state=async_mock.CoroutineMock(), - ) + with self.assertRaises(test_module.IndyIssuerError): + await self.issuer.create_credential( + test_schema, + test_offer, + test_request, + test_values, ) - with self.assertRaises(test_module.IndyIssuerError): - await self.issuer.create_credential( - test_schema, - test_offer, - test_request, - test_values, - "dummy-cxid", - ) - @async_mock.patch("indy.anoncreds.issuer_create_and_store_revoc_reg") @async_mock.patch.object(test_module, "create_tails_writer", autospec=True) async def test_create_and_store_revocation_registry( diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py index 2a1c37dd38..90d1a15217 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py @@ -7,6 +7,7 @@ from typing import Mapping, Optional, Tuple from ....cache.base import BaseCache +from ....connections.models.conn_record import ConnRecord from ....core.error import BaseError from ....core.profile import Profile from ....indy.holder import IndyHolder, IndyHolderError @@ -23,10 +24,10 @@ from ....messaging.responder import BaseResponder from ....multitenant.base import BaseMultitenantManager from ....revocation.indy import IndyRevocation +from ....revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ....revocation.models.revocation_registry import RevocationRegistry from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError -from ....connections.models.conn_record import ConnRecord from ...out_of_band.v1_0.models.oob_record import OobRecord from .messages.credential_ack import CredentialAck @@ -638,7 +639,7 @@ async def issue_credential( await asyncio.sleep(2) if revocable: - revoc = IndyRevocation(self.profile) + revoc = IndyRevocation(self._profile) registry_info = await revoc.get_or_create_active_registry( cred_def_id ) @@ -658,7 +659,6 @@ async def issue_credential( cred_offer_ser, cred_req_ser, cred_values, - cred_ex_record.credential_exchange_id, rev_reg_id, tails_path, ) @@ -667,7 +667,7 @@ async def issue_credential( continue if revocable and rev_reg.max_creds <= int(cred_rev_id): - revoc = IndyRevocation(self.profile) + revoc = IndyRevocation(self._profile) await revoc.handle_full_registry(rev_reg_id) del revoc @@ -681,6 +681,22 @@ async def issue_credential( ) from None async with self._profile.transaction() as txn: + if revocable and cred_rev_id: + issuer_cr_rec = IssuerCredRevRecord( + state=IssuerCredRevRecord.STATE_ISSUED, + cred_ex_id=cred_ex_record.credential_exchange_id, + cred_ex_version=IssuerCredRevRecord.VERSION_1, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await issuer_cr_rec.save( + txn, + reason=( + "Created issuer cred rev record for " + f"rev reg id {rev_reg_id}, index {cred_rev_id}" + ), + ) + cred_ex_record = await V10CredentialExchange.retrieve_by_id( txn, cred_ex_record.credential_exchange_id, for_update=True ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py index ba0e65f924..94ec08f0f6 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py @@ -945,7 +945,6 @@ async def test_issue_credential_revocable(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, REV_REG_ID, "dummy-path", ) @@ -1031,7 +1030,6 @@ async def test_issue_credential_non_revocable(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, None, None, ) @@ -1113,7 +1111,6 @@ async def test_issue_credential_fills_rr(self): INDY_OFFER, INDY_CRED_REQ, cred_values, - stored_exchange.credential_exchange_id, REV_REG_ID, "dummy-path", ) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py index fbd2882b7d..7ef901260f 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -25,8 +25,9 @@ ) from ......messaging.decorators.attach_decorator import AttachDecorator from ......multitenant.base import BaseMultitenantManager -from ......revocation.models.revocation_registry import RevocationRegistry from ......revocation.indy import IndyRevocation +from ......revocation.models.issuer_cred_rev_record import IssuerCredRevRecord +from ......revocation.models.revocation_registry import RevocationRegistry from ......storage.base import BaseStorage from ...message_types import ( @@ -352,6 +353,7 @@ async def issue_credential( schema = await ledger.get_schema(schema_id) cred_def = await ledger.get_credential_definition(cred_def_id) revocable = cred_def["value"].get("revocation") + result = None for attempt in range(max(retries, 1)): if attempt > 0: @@ -380,7 +382,6 @@ async def issue_credential( cred_offer, cred_request, cred_values, - cred_ex_record.cred_ex_id, rev_reg_id, tails_path, ) @@ -388,24 +389,45 @@ async def issue_credential( # unlucky, another instance filled the registry first continue - detail_record = V20CredExRecordIndy( - cred_ex_id=cred_ex_record.cred_ex_id, - rev_reg_id=rev_reg_id, - cred_rev_id=cred_rev_id, - ) - async with self._profile.session() as session: - await detail_record.save(session, reason="v2.0 issue credential") - if revocable and rev_reg.max_creds <= int(cred_rev_id): revoc = IndyRevocation(self.profile) await revoc.handle_full_registry(rev_reg_id) del revoc - return self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) + result = self.get_format_data(CRED_20_ISSUE, json.loads(cred_json)) + break - raise V20CredFormatError( - f"Cred def '{cred_def_id}' has no active revocation registry" - ) + if not result: + raise V20CredFormatError( + f"Cred def '{cred_def_id}' has no active revocation registry" + ) + + async with self._profile.transaction() as txn: + detail_record = V20CredExRecordIndy( + cred_ex_id=cred_ex_record.cred_ex_id, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await detail_record.save(txn, reason="v2.0 issue credential") + + if revocable and cred_rev_id: + issuer_cr_rec = IssuerCredRevRecord( + state=IssuerCredRevRecord.STATE_ISSUED, + cred_ex_id=cred_ex_record.cred_ex_id, + cred_ex_version=IssuerCredRevRecord.VERSION_2, + rev_reg_id=rev_reg_id, + cred_rev_id=cred_rev_id, + ) + await issuer_cr_rec.save( + txn, + reason=( + "Created issuer cred rev record for " + f"rev reg id {rev_reg_id}, index {cred_rev_id}" + ), + ) + await txn.commit() + + return result async def receive_credential( self, cred_ex_record: V20CredExRecord, cred_issue_message: V20CredIssue diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py index e97fd6d457..06028fa00e 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/indy/tests/test_handler.py @@ -753,7 +753,6 @@ async def test_issue_credential_revocable(self): INDY_OFFER, INDY_CRED_REQ, attr_values, - cred_ex_record.cred_ex_id, REV_REG_ID, "dummy-path", ) @@ -837,7 +836,6 @@ async def test_issue_credential_non_revocable(self): INDY_OFFER, INDY_CRED_REQ, attr_values, - cred_ex_record.cred_ex_id, None, None, ) diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index 4057d7a799..10db676f70 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -297,6 +297,7 @@ async def set_cred_revoked_state( txn, rev_reg_id, cred_rev_id, for_update=True ) cred_ex_id = rev_rec.cred_ex_id + cred_ex_version = rev_rec.cred_ex_version rev_rec.state = IssuerCredRevRecord.STATE_REVOKED await rev_rec.save(txn, reason="revoke credential") await txn.commit() @@ -304,25 +305,33 @@ async def set_cred_revoked_state( continue async with self._profile.transaction() as txn: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V10CredentialExchange.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - continue # skip 2.0 record check - except StorageNotFoundError: - pass - - try: - cred_ex_record = await V20CredExRecord.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - except StorageNotFoundError: - pass + if ( + not cred_ex_version + or cred_ex_version == IssuerCredRevRecord.VERSION_1 + ): + try: + cred_ex_record = await V10CredentialExchange.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = ( + V10CredentialExchange.STATE_CREDENTIAL_REVOKED + ) + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + continue # skip 2.0 record check + except StorageNotFoundError: + pass + + if ( + not cred_ex_version + or cred_ex_version == IssuerCredRevRecord.VERSION_2 + ): + try: + cred_ex_record = await V20CredExRecord.retrieve_by_id( + txn, cred_ex_id, for_update=True + ) + cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + except StorageNotFoundError: + pass diff --git a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py index 5ab8d3ba09..10ba69ef53 100644 --- a/aries_cloudagent/revocation/models/issuer_cred_rev_record.py +++ b/aries_cloudagent/revocation/models/issuer_cred_rev_record.py @@ -27,6 +27,7 @@ class Meta: RECORD_TOPIC = "issuer_cred_rev" TAG_NAMES = { "cred_ex_id", + "cred_ex_version", "cred_def_id", "rev_reg_id", "cred_rev_id", @@ -36,6 +37,9 @@ class Meta: STATE_ISSUED = "issued" STATE_REVOKED = "revoked" + VERSION_1 = "1" + VERSION_2 = "2" + def __init__( self, *, @@ -45,6 +49,7 @@ def __init__( rev_reg_id: str = None, cred_rev_id: str = None, cred_def_id: str = None, # Marshmallow formalism: leave None + cred_ex_version: str = None, **kwargs, ): """Initialize a new IssuerCredRevRecord.""" @@ -53,6 +58,7 @@ def __init__( self.rev_reg_id = rev_reg_id self.cred_rev_id = cred_rev_id self.cred_def_id = ":".join(rev_reg_id.split(":")[-7:-2]) + self.cred_ex_version = cred_ex_version @property def record_id(self) -> str: @@ -158,3 +164,7 @@ class Meta: description="Credential revocation identifier", **INDY_CRED_REV_ID, ) + cred_ex_version = fields.Str( + required=False, + description="Credential exchange version", + ) diff --git a/aries_cloudagent/revocation/tests/test_manager.py b/aries_cloudagent/revocation/tests/test_manager.py index 113c4850d1..ec026b9afd 100644 --- a/aries_cloudagent/revocation/tests/test_manager.py +++ b/aries_cloudagent/revocation/tests/test_manager.py @@ -2,7 +2,6 @@ from asynctest import mock as async_mock from asynctest import TestCase as AsyncTestCase -from more_itertools import side_effect from aries_cloudagent.revocation.models.issuer_cred_rev_record import ( IssuerCredRevRecord, @@ -13,6 +12,8 @@ from ...protocols.issue_credential.v1_0.models.credential_exchange import ( V10CredentialExchange, ) +from ...protocols.issue_credential.v2_0.models.cred_ex_record import V20CredExRecord + from ..manager import RevocationManager, RevocationManagerError @@ -427,7 +428,7 @@ async def test_retrieve_records(self): assert ret_ex.connection_id == str(index) assert ret_ex.thread_id == str(1000 + index) - async def test_set_revoked_state(self): + async def test_set_revoked_state_v1(self): CRED_REV_ID = "1" async with self.profile.session() as session: @@ -466,3 +467,40 @@ async def test_set_revoked_state(self): session, crev_record.record_id ) assert check_crev_record.state == IssuerCredRevRecord.STATE_REVOKED + + async def test_set_revoked_state_v2(self): + CRED_REV_ID = "1" + + async with self.profile.session() as session: + exchange_record = V20CredExRecord( + connection_id="mark-revoked-cid", + thread_id="mark-revoked-tid", + initiator=V20CredExRecord.INITIATOR_SELF, + role=V20CredExRecord.ROLE_ISSUER, + state=V20CredExRecord.STATE_ISSUED, + ) + await exchange_record.save(session) + + crev_record = IssuerCredRevRecord( + cred_ex_id=exchange_record.cred_ex_id, + cred_def_id=CRED_DEF_ID, + rev_reg_id=REV_REG_ID, + cred_rev_id=CRED_REV_ID, + state=IssuerCredRevRecord.STATE_ISSUED, + ) + await crev_record.save(session) + + await self.manager.set_cred_revoked_state(REV_REG_ID, [CRED_REV_ID]) + + async with self.profile.session() as session: + check_exchange_record = await V20CredExRecord.retrieve_by_id( + session, exchange_record.cred_ex_id + ) + assert ( + check_exchange_record.state == V20CredExRecord.STATE_CREDENTIAL_REVOKED + ) + + check_crev_record = await IssuerCredRevRecord.retrieve_by_id( + session, crev_record.record_id + ) + assert check_crev_record.state == IssuerCredRevRecord.STATE_REVOKED diff --git a/aries_cloudagent/storage/in_memory.py b/aries_cloudagent/storage/in_memory.py index c94a9d6f93..296858f1d2 100644 --- a/aries_cloudagent/storage/in_memory.py +++ b/aries_cloudagent/storage/in_memory.py @@ -70,8 +70,7 @@ async def get_record( row = self.profile.records.get(record_id) if row and row.type == record_type: return row - if not row: - raise StorageNotFoundError("Record not found: {}".format(record_id)) + raise StorageNotFoundError("Record not found: {}".format(record_id)) async def update_record(self, record: StorageRecord, value: str, tags: Mapping): """