From f8efffdf3a267cb7fe160da172383a6c550adb27 Mon Sep 17 00:00:00 2001 From: Jamie Hale Date: Mon, 21 Oct 2024 20:46:46 +0000 Subject: [PATCH] refactor / fix test Signed-off-by: Jamie Hale --- .../models/issuer_rev_reg_record.py | 157 +++++++++--------- .../tests/test_issuer_rev_reg_record.py | 107 ++++-------- 2 files changed, 111 insertions(+), 153 deletions(-) diff --git a/acapy_agent/revocation/models/issuer_rev_reg_record.py b/acapy_agent/revocation/models/issuer_rev_reg_record.py index 5837633e37..bed21031a2 100644 --- a/acapy_agent/revocation/models/issuer_rev_reg_record.py +++ b/acapy_agent/revocation/models/issuer_rev_reg_record.py @@ -362,6 +362,19 @@ async def send_entry( return rev_entry_res + def _get_revoked_discrepancies( + self, recs: Sequence[IssuerCredRevRecord], rev_reg_delta: dict + ) -> Tuple[list, int]: + revoked_ids = [] + rec_count = 0 + for rec in recs: + if rec.state == IssuerCredRevRecord.STATE_REVOKED: + revoked_ids.append(int(rec.cred_rev_id)) + if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: + rec_count += 1 + + return revoked_ids, rec_count + async def fix_ledger_entry( self, profile: Profile, @@ -369,100 +382,88 @@ async def fix_ledger_entry( genesis_transactions: str, ) -> Tuple[dict, dict, dict]: """Fix the ledger entry to match wallet-recorded credentials.""" + recovery_txn = {} + applied_txn = {} + # get rev reg delta (revocations published to ledger) ledger = profile.inject(BaseLedger) async with ledger: (rev_reg_delta, _) = await ledger.get_revoc_reg_delta(self.revoc_reg_id) # get rev reg records from wallet (revocations and status) - recs = [] - rec_count = 0 - accum_count = 0 - recovery_txn = {} - applied_txn = {} async with profile.session() as session: recs = await IssuerCredRevRecord.query_by_ids( session, rev_reg_id=self.revoc_reg_id ) - revoked_ids = [] - for rec in recs: - if rec.state == IssuerCredRevRecord.STATE_REVOKED: - revoked_ids.append(int(rec.cred_rev_id)) - if int(rec.cred_rev_id) not in rev_reg_delta["value"]["revoked"]: - # await rec.set_state(session, IssuerCredRevRecord.STATE_ISSUED) - rec_count += 1 + revoked_ids, rec_count = self._get_revoked_discrepancies(recs, rev_reg_delta) + + LOGGER.debug(f"Fixed entry recs count = {rec_count}") + LOGGER.debug(f"Rev reg entry value: {self.revoc_reg_entry.value}") + LOGGER.debug(f'Rev reg delta: {rev_reg_delta.get("value")}') + + # No update required if no discrepancies + if rec_count == 0: + return (rev_reg_delta, {}, {}) + + # We have revocation discrepancies, generate the recovery txn + async with profile.session() as session: + # We need the cred_def and rev_reg_def_private to generate the recovery txn + issuer_rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( + session, self.revoc_reg_id + ) + cred_def_id = issuer_rev_reg_record.cred_def_id + cred_def = await session.handle.fetch(CATEGORY_CRED_DEF, cred_def_id) + rev_reg_def_private = await session.handle.fetch( + CATEGORY_REV_REG_DEF_PRIVATE, self.revoc_reg_id + ) - LOGGER.debug(">>> fixed entry recs count = %s", rec_count) - LOGGER.debug( - ">>> rev_reg_record.revoc_reg_entry.value: %s", - self.revoc_reg_entry.value, + credx_module = importlib.import_module("indy_credx") + cred_defn = credx_module.CredentialDefinition.load(cred_def.value_json) + rev_reg_defn_private = credx_module.RevocationRegistryDefinitionPrivate.load( + rev_reg_def_private.value_json + ) + calculated_txn = await generate_ledger_rrrecovery_txn( + genesis_transactions, + self.revoc_reg_id, + revoked_ids, + cred_defn, + rev_reg_defn_private, ) - LOGGER.debug('>>> rev_reg_delta.get("value"): %s', rev_reg_delta.get("value")) - - # if we had any revocation discrepancies, check the accumulator value - if rec_count > 0: - if (self.revoc_reg_entry.value and rev_reg_delta.get("value")) and not ( - self.revoc_reg_entry.value.accum == rev_reg_delta["value"]["accum"] - ): - # self.revoc_reg_entry = rev_reg_delta["value"] - # await self.save(session) - accum_count += 1 + recovery_txn = json.loads(calculated_txn.to_json()) + + LOGGER.debug(f"Applying ledger update: {apply_ledger_update}") + if apply_ledger_update: + async with profile.session() as session: + ledger = session.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: + ledger_response = await ledger.send_revoc_reg_entry( + self.revoc_reg_id, "CL_ACCUM", recovery_txn + ) + + applied_txn = ledger_response["result"] + + # Update the local wallets rev reg entry with the new accumulator value async with profile.session() as session: - issuer_rev_reg_record = await IssuerRevRegRecord.retrieve_by_revoc_reg_id( - session, self.revoc_reg_id + rev_reg = await session.handle.fetch( + CATEGORY_REV_REG, self.revoc_reg_id, for_update=True ) - cred_def_id = issuer_rev_reg_record.cred_def_id - _cred_def = await session.handle.fetch(CATEGORY_CRED_DEF, cred_def_id) - _rev_reg_def_private = await session.handle.fetch( - CATEGORY_REV_REG_DEF_PRIVATE, self.revoc_reg_id + new_value_json = rev_reg.value_json + new_value_json["value"]["accum"] = applied_txn["txn"]["data"]["value"][ + "accum" + ] + await session.handle.replace( + CATEGORY_REV_REG, + rev_reg.name, + json.dumps(new_value_json), + rev_reg.tags, ) - credx_module = importlib.import_module("indy_credx") - cred_defn = credx_module.CredentialDefinition.load(_cred_def.value_json) - rev_reg_defn_private = credx_module.RevocationRegistryDefinitionPrivate.load( - _rev_reg_def_private.value_json - ) - calculated_txn = await generate_ledger_rrrecovery_txn( - genesis_transactions, - self.revoc_reg_id, - revoked_ids, - cred_defn, - rev_reg_defn_private, - ) - recovery_txn = json.loads(calculated_txn.to_json()) - - LOGGER.debug(">>> apply_ledger_update = %s", apply_ledger_update) - if apply_ledger_update: - async with profile.session() as session: - ledger = session.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: - ledger_response = await ledger.send_revoc_reg_entry( - self.revoc_reg_id, "CL_ACCUM", recovery_txn - ) - - applied_txn = ledger_response["result"] - - # Update the wallets rev reg entry with the new accumulator value - async with profile.session() as session: - rev_reg = await session.handle.fetch( - CATEGORY_REV_REG, self.revoc_reg_id, for_update=True - ) - new_value_json = rev_reg.value_json - new_value_json["value"]["accum"] = applied_txn["txn"]["data"][ - "value" - ]["accum"] - await session.handle.replace( - CATEGORY_REV_REG, - rev_reg.name, - json.dumps(new_value_json), - rev_reg.tags, - ) return (rev_reg_delta, recovery_txn, applied_txn) diff --git a/acapy_agent/revocation/models/tests/test_issuer_rev_reg_record.py b/acapy_agent/revocation/models/tests/test_issuer_rev_reg_record.py index 705ed5a49f..6aa6cdac7c 100644 --- a/acapy_agent/revocation/models/tests/test_issuer_rev_reg_record.py +++ b/acapy_agent/revocation/models/tests/test_issuer_rev_reg_record.py @@ -1,14 +1,12 @@ import importlib import json from os.path import join -from typing import Any, Mapping, Optional, Type from unittest import IsolatedAsyncioTestCase from acapy_agent.tests import mock -from ....config.injection_context import InjectionContext -from ....core.in_memory import InMemoryProfile, InMemoryProfileSession -from ....core.profile import Profile, ProfileSession +from ....core.in_memory import InMemoryProfile +from ....core.in_memory.profile import InMemoryProfileSession from ....indy.issuer import IndyIssuer, IndyIssuerError from ....indy.util import indy_client_dir from ....ledger.base import BaseLedger @@ -40,6 +38,7 @@ }, "tailsHash": TAILS_HASH, "tailsLocation": TAILS_URI, + "accum": "21 11792B036AED0AAA12A46CF39347EB35C865DAC99F767B286F6E37FF0FF4F1CBE 21 12571556D2A1B4475E81295FC8A4F0B66D00FB78EE8C7E15C29C2CA862D0217D4 6 92166D2C2A3BC621AD615136B7229AF051AB026704BF8874F9F0B0106122BF4F 4 2C47BCBBC32904161E2A2926F120AD8F40D94C09D1D97DA735191D27370A68F8F 6 8CC19FDA63AB16BEA45050D72478115BC1CCB8E47A854339D2DD5E112976FFF7 4 298B2571FFC63A737B79C131AC7048A1BD474BF907AF13BC42E533C79FB502C7", }, } REV_REG_ENTRY = { @@ -80,7 +79,8 @@ async def test_order(self): await rec1.save(session, reason="another record") assert rec0 < rec1 - async def test_fix_ledger_entry(self): + @mock.patch.object(InMemoryProfileSession, "handle") + async def test_fix_ledger_entry(self, mock_handle): mock_cred_def = { "ver": "1.0", "id": "55GkHamhTU1ZbTbV2ab9DE:3:CL:15:tag", @@ -119,73 +119,14 @@ async def test_fix_ledger_entry(self): } } - class TestProfile(InMemoryProfile): - def session( - self, context: Optional[InjectionContext] = None - ) -> "ProfileSession": - return TestProfileSession(self, context=context) - - @classmethod - def test_profile( - cls, settings: Mapping[str, Any] = None, bind: Mapping[Type, Any] = None - ) -> "TestProfile": - profile = TestProfile( - context=InjectionContext(enforce_typing=False, settings=settings), - name=InMemoryProfile.TEST_PROFILE_NAME, - ) - if bind: - for k, v in bind.items(): - if v: - profile.context.injector.bind_instance(k, v) - else: - profile.context.injector.clear_binding(k) - return profile - - @classmethod - def test_session( - cls, settings: Mapping[str, Any] = None, bind: Mapping[Type, Any] = None - ) -> "TestProfileSession": - session = TestProfileSession(cls.test_profile(), settings=settings) - session._active = True - session._init_context() - if bind: - for k, v in bind.items(): - if v: - session.context.injector.bind_instance(k, v) - else: - session.context.injector.clear_binding(k) - return session - - class TestProfileSession(InMemoryProfileSession): - def __init__( - self, - profile: Profile, - *, - context: Optional[InjectionContext] = None, - settings: Mapping[str, Any] = None, - ): - super().__init__(profile=profile, context=context, settings=settings) - self.handle_counter = 0 - - @property - def handle(self): - if self.handle_counter == 0: - self.handle_counter = self.handle_counter + 1 - return mock.MagicMock( - fetch=mock.CoroutineMock( - return_value=mock.MagicMock( - value_json=json.dumps(mock_cred_def) - ) - ) - ) - else: - return mock.MagicMock( - fetch=mock.CoroutineMock( - return_value=mock.MagicMock( - value_json=json.dumps(mock_reg_rev_def_private), - ), - ) - ) + mock_handle.fetch = mock.CoroutineMock( + side_effect=[ + mock.MagicMock(value_json=json.dumps(mock_cred_def)), + mock.MagicMock(value_json=json.dumps(mock_reg_rev_def_private)), + mock.MagicMock(value_json=REV_REG_DEF), + ] + ) + mock_handle.replace = mock.CoroutineMock() credx_module = importlib.import_module("indy_credx") rev_reg_delta_json = json.dumps( @@ -225,10 +166,18 @@ def handle(self): ) self.ledger.send_revoc_reg_entry = mock.CoroutineMock( return_value={ - "result": {"...": "..."}, + "result": { + "txn": { + "data": { + "value": { + "accum": "1 0792BD1C8C1A529173FDF54A5B30AC90C2472956622E9F04971D36A9BF77C2C5 1 13B18B6B68AD62605C74FD61088814338EDEEB41C2195F96EC0E83B2B3D0258F 1 102ED0DDE96F6367199CE1C0B138F172BC913B65E37250581606974034F4CA20 1 1C53786D2C15190B57167CDDD2A046CAD63970B5DE43F4D492D4F46B8EEE6FF1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000" + } + } + } + }, }, ) - _test_session = TestProfile.test_session( + _test_session = InMemoryProfile.test_session( settings={"tails_server_base_url": "http://1.2.3.4:8088"}, ) _test_profile = _test_session.profile @@ -264,7 +213,15 @@ def handle(self): "accum": "1 0792BD1C8C1A529173FDF54A5B30AC90C2472956622E9F04971D36A9BF77C2C5 1 13B18B6B68AD62605C74FD61088814338EDEEB41C2195F96EC0E83B2B3D0258F 1 102ED0DDE96F6367199CE1C0B138F172BC913B65E37250581606974034F4CA20 1 1C53786D2C15190B57167CDDD2A046CAD63970B5DE43F4D492D4F46B8EEE6FF1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000" }, }, - {"...": "..."}, + { + "txn": { + "data": { + "value": { + "accum": "1 0792BD1C8C1A529173FDF54A5B30AC90C2472956622E9F04971D36A9BF77C2C5 1 13B18B6B68AD62605C74FD61088814338EDEEB41C2195F96EC0E83B2B3D0258F 1 102ED0DDE96F6367199CE1C0B138F172BC913B65E37250581606974034F4CA20 1 1C53786D2C15190B57167CDDD2A046CAD63970B5DE43F4D492D4F46B8EEE6FF1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000" + }, + } + } + }, ) == await rec.fix_ledger_entry( profile=_test_profile, apply_ledger_update=True,