Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revocation API using anoncreds-rs #2458

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/post-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ WORKSPACE_DIR=$(pwd)
# install all ACA-Py requirements
python -m pip install --upgrade pip
pip3 install -r requirements.txt -r requirements.askar.txt -r requirements.bbs.txt -r requirements.dev.txt -r requirements.indy.txt -r requirements.anoncreds.txt
pip3 install -r demo/requirements.behave.txt -r demo/requirements.txt

# install black for formatting
pip3 install black
8 changes: 7 additions & 1 deletion aries_cloudagent/anoncreds/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Base Registry."""
from abc import ABC, abstractmethod
from typing import Generic, Optional, Pattern, Sequence, TypeVar
from typing import Generic, Optional, Pattern, Sequence, Tuple, TypeVar

from ..config.injection_context import InjectionContext
from ..core.error import BaseError
Expand Down Expand Up @@ -136,6 +136,12 @@ async def get_revocation_list(
) -> GetRevListResult:
"""Get a revocation list from the registry."""

@abstractmethod
async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
) -> Tuple[dict, int]:
"""Get a revocation registry delta."""

dbluhm marked this conversation as resolved.
Show resolved Hide resolved

class BaseAnonCredsRegistrar(BaseAnonCredsHandler):
"""Base Anon Creds Registrar."""
Expand Down
8 changes: 7 additions & 1 deletion aries_cloudagent/anoncreds/default/did_indy/registry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""DID Indy Registry."""
import logging
import re
from typing import Optional, Pattern, Sequence
from typing import Optional, Pattern, Sequence, Tuple

from ....config.injection_context import InjectionContext
from ....core.profile import Profile
Expand Down Expand Up @@ -98,6 +98,12 @@ async def get_revocation_list(
"""Get a revocation list from the registry."""
raise NotImplementedError()

async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
) -> Tuple[dict, int]:
"""Get a revocation registry delta."""
raise NotImplementedError()

dbluhm marked this conversation as resolved.
Show resolved Hide resolved
async def register_revocation_list(
self,
profile: Profile,
Expand Down
8 changes: 7 additions & 1 deletion aries_cloudagent/anoncreds/default/did_web/registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""DID Web Registry."""
import re
from typing import Optional, Pattern, Sequence
from typing import Optional, Pattern, Sequence, Tuple

from ....config.injection_context import InjectionContext
from ....core.profile import Profile
Expand Down Expand Up @@ -93,6 +93,12 @@ async def get_revocation_list(
"""Get a revocation list from the registry."""
raise NotImplementedError()

async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
) -> Tuple[dict, int]:
"""Get a revocation registry delta."""
raise NotImplementedError()

dbluhm marked this conversation as resolved.
Show resolved Hide resolved
async def register_revocation_list(
self,
profile: Profile,
Expand Down
27 changes: 22 additions & 5 deletions aries_cloudagent/anoncreds/default/legacy_indy/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,7 @@ def _indexes_to_bit_array(self, indexes: List[int], size: int) -> List[int]:
"""Turn a sequence of indexes into a full state bit array."""
return [1 if index in indexes else 0 for index in range(1, size + 1)]

async def get_revocation_list(
self, profile: Profile, rev_reg_def_id: str, timestamp: int
) -> GetRevListResult:
"""Get a revocation list from the registry."""
async def _get_ledger(self, profile: Profile, rev_reg_def_id: str):
async with profile.session() as session:
multitenant_mgr = session.inject_or(BaseMultitenantManager)
if multitenant_mgr:
Expand All @@ -512,6 +509,14 @@ async def get_revocation_list(
reason += ": missing wallet-type?"
raise AnonCredsResolutionError(reason)

return ledger_id, ledger

async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
) -> Tuple[dict, int]:
"""Fetch the revocation registry delta."""
ledger_id, ledger = await self._get_ledger(profile, rev_reg_def_id)

async with ledger:
delta, timestamp = await ledger.get_revoc_reg_delta(
rev_reg_def_id, timestamp_to=timestamp
Expand All @@ -522,8 +527,20 @@ async def get_revocation_list(
f"Revocation list not found for rev reg def: {rev_reg_def_id}",
{"ledger_id": ledger_id},
)
LOGGER.debug("Retrieved delta: %s", delta)
return delta, timestamp

async def get_revocation_list(
self, profile: Profile, rev_reg_def_id: str, timestamp: int
) -> GetRevListResult:
"""Get the revocation registry list."""
_, ledger = await self._get_ledger(profile, rev_reg_def_id)

LOGGER.debug("Retrieved delta: %s", delta)
delta, timestamp = await self.get_revocation_registry_delta(
profile, rev_reg_def_id, timestamp
)

async with ledger:
max_cred_num = await self._get_or_fetch_rev_reg_def_max_cred_num(
profile, ledger, rev_reg_def_id
)
Expand Down
11 changes: 10 additions & 1 deletion aries_cloudagent/anoncreds/registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""AnonCreds Registry."""
import logging
from typing import List, Optional, Sequence
from typing import List, Optional, Sequence, Tuple


from ..core.profile import Profile
Expand Down Expand Up @@ -147,6 +147,15 @@ async def get_revocation_list(
resolver = await self._resolver_for_identifier(rev_reg_def_id)
return await resolver.get_revocation_list(profile, rev_reg_def_id, timestamp)

async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
) -> Tuple[dict, int]:
"""Get a revocation registry delta."""
resolver = await self._resolver_for_identifier(rev_reg_def_id)
return await resolver.get_revocation_registry_delta(
profile, rev_reg_def_id, timestamp
)

dbluhm marked this conversation as resolved.
Show resolved Hide resolved
async def register_revocation_list(
self,
profile: Profile,
Expand Down
24 changes: 24 additions & 0 deletions aries_cloudagent/anoncreds/revocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,22 @@ async def get_created_revocation_registry_definitions(
# entry.name was stored as the credential_definition's ID
return [entry.name for entry in rev_reg_defs]

async def get_created_revocation_registry_definition_state(
self,
rev_reg_def_id: str,
) -> Optional[str]:
"""Retrieve rev reg def by ID from rev reg defs previously created."""
async with self.profile.session() as session:
rev_reg_def_entry = await session.handle.fetch(
CATEGORY_REV_REG_DEF,
name=rev_reg_def_id,
)

if rev_reg_def_entry:
return rev_reg_def_entry.tags.get("state")

return None

async def get_created_revocation_registry_definition(
self,
rev_reg_def_id: str,
Expand Down Expand Up @@ -1140,3 +1156,11 @@ async def clear_pending_revocations(
value_json=value,
tags=tags,
)

async def set_tails_file_public_uri(self, rev_reg_id, tails_public_uri):
"""Update Revocation Registy tails file public uri."""
pass

async def set_rev_reg_state(self, rev_reg_id, state):
"""Update Revocation Registy state."""
pass
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from asynctest import TestCase as AsyncTestCase
from asynctest import mock as async_mock
import pytest

from ....admin.request_context import AdminRequestContext
from ....core.in_memory import InMemoryProfile
Expand Down Expand Up @@ -63,7 +64,8 @@ def setUp(self):
__getitem__=lambda _, k: self.request_dict[k],
)

async def anoncreds_break_test_send_credential_definition(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition(self):
self.request.json = async_mock.CoroutineMock(
return_value={
"schema_id": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0",
Expand All @@ -75,8 +77,10 @@ async def anoncreds_break_test_send_credential_definition(self):
self.request.query = {"create_transaction_for_endorser": "false"}

with async_mock.patch.object(test_module.web, "json_response") as mock_response:
result = await test_module.anoncreds_break_credential_definitions_send_credential_definition(
self.request
result = (
await test_module.credential_definitions_send_credential_definition(
self.request
)
)
assert result == mock_response.return_value
mock_response.assert_called_once_with(
Expand All @@ -86,7 +90,8 @@ async def anoncreds_break_test_send_credential_definition(self):
}
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand Down Expand Up @@ -124,8 +129,10 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
}
)
)
result = await test_module.anoncreds_break_credential_definitions_send_credential_definition(
self.request
result = (
await test_module.credential_definitions_send_credential_definition(
self.request
)
)
assert result == mock_response.return_value
mock_response.assert_called_once_with(
Expand All @@ -135,7 +142,8 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
}
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser_storage_x(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser_storage_x(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand Down Expand Up @@ -171,11 +179,12 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
)

with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser_not_found_x(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser_not_found_x(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand All @@ -197,11 +206,12 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
mock_conn_rec_retrieve.side_effect = test_module.StorageNotFoundError()

with self.assertRaises(test_module.web.HTTPNotFound):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser_base_model_x(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser_base_model_x(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand All @@ -223,11 +233,12 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
mock_conn_rec_retrieve.side_effect = test_module.BaseModelError()

with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser_no_endorser_info_x(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser_no_endorser_info_x(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand All @@ -250,11 +261,12 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
metadata_get=async_mock.CoroutineMock(return_value=None)
)
with self.assertRaises(test_module.web.HTTPForbidden):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_create_transaction_for_endorser_no_endorser_did_x(
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_create_transaction_for_endorser_no_endorser_did_x(
self,
):
self.request.json = async_mock.CoroutineMock(
Expand All @@ -281,11 +293,12 @@ async def anoncreds_break_test_send_credential_definition_create_transaction_for
)
)
with self.assertRaises(test_module.web.HTTPForbidden):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_no_ledger(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_no_ledger(self):
self.request.json = async_mock.CoroutineMock(
return_value={
"schema_id": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0",
Expand All @@ -297,11 +310,12 @@ async def anoncreds_break_test_send_credential_definition_no_ledger(self):
self.context.injector.clear_binding(BaseLedger)
self.profile_injector.clear_binding(BaseLedger)
with self.assertRaises(test_module.web.HTTPForbidden):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_send_credential_definition_ledger_x(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_send_credential_definition_ledger_x(self):
self.request.json = async_mock.CoroutineMock(
return_value={
"schema_id": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0",
Expand All @@ -316,11 +330,12 @@ async def anoncreds_break_test_send_credential_definition_ledger_x(self):
side_effect=test_module.LedgerError("oops")
)
with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.anoncreds_break_credential_definitions_send_credential_definition(
await test_module.credential_definitions_send_credential_definition(
self.request
)

async def anoncreds_break_test_created(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_created(self):
self.request.match_info = {"cred_def_id": CRED_DEF_ID}

with async_mock.patch.object(test_module.web, "json_response") as mock_response:
Expand All @@ -330,7 +345,8 @@ async def anoncreds_break_test_created(self):
{"credential_definition_ids": [CRED_DEF_ID]}
)

async def anoncreds_break_test_get_credential_definition(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_get_credential_definition(self):
self.profile_injector.bind_instance(
IndyLedgerRequestsExecutor,
async_mock.MagicMock(
Expand All @@ -352,7 +368,8 @@ async def anoncreds_break_test_get_credential_definition(self):
}
)

async def anoncreds_break_test_get_credential_definition_multitenant(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_get_credential_definition_multitenant(self):
self.profile_injector.bind_instance(
BaseMultitenantManager,
async_mock.MagicMock(MultitenantManager, autospec=True),
Expand All @@ -374,7 +391,8 @@ async def anoncreds_break_test_get_credential_definition_multitenant(self):
}
)

async def anoncreds_break_test_get_credential_definition_no_ledger(self):
@pytest.mark.skip(reason="anoncreds-rs breaking change")
async def test_get_credential_definition_no_ledger(self):
self.profile_injector.bind_instance(
IndyLedgerRequestsExecutor,
async_mock.MagicMock(
Expand Down
Loading