Skip to content

Commit

Permalink
Fix publishing multiple rev reg defs with endorsement (#3107)
Browse files Browse the repository at this point in the history
* Fix publishing multiple rev reg defs with endorsement

Signed-off-by: jamshale <jamiehalebc@gmail.com>

* Refactor / Unit test

Signed-off-by: jamshale <jamiehalebc@gmail.com>

* Add more thourough unit testing

Signed-off-by: jamshale <jamiehalebc@gmail.com>

* Revert auto formatting to avoid doing coverage

Signed-off-by: jamshale <jamiehalebc@gmail.com>

---------

Signed-off-by: jamshale <jamiehalebc@gmail.com>
  • Loading branch information
jamshale committed Jul 18, 2024
1 parent 0c0ddce commit f3a10ba
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 46 deletions.
18 changes: 11 additions & 7 deletions aries_cloudagent/revocation/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ async def publish_pending_revocations(
"""
result = {}
issuer = self._profile.inject(IndyIssuer)
rev_entry_resp = None
rev_entry_responses = []
async with self._profile.session() as session:
issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(session)

Expand Down Expand Up @@ -331,20 +331,24 @@ async def publish_pending_revocations(
session, "endorser_info"
)
endorser_did = endorser_info["endorser_did"]
rev_entry_resp = await issuer_rr_upd.send_entry(
self._profile,
write_ledger=write_ledger,
endorser_did=endorser_did,
rev_entry_responses.append(
await issuer_rr_upd.send_entry(
self._profile,
write_ledger=write_ledger,
endorser_did=endorser_did,
)
)
else:
rev_entry_resp = await issuer_rr_upd.send_entry(self._profile)
rev_entry_responses.append(
await issuer_rr_upd.send_entry(self._profile)
)
await notify_revocation_published_event(
self._profile, issuer_rr_rec.revoc_reg_id, crids
)
published = sorted(crid for crid in crids if crid not in failed_crids)
result[issuer_rr_rec.revoc_reg_id] = published

return rev_entry_resp, result
return rev_entry_responses, result

async def clear_pending_revocations(
self, purge: Mapping[Text, Sequence[Text]] = None
Expand Down
34 changes: 27 additions & 7 deletions aries_cloudagent/revocation/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,25 +652,44 @@ async def publish_revocations(request: web.BaseRequest):
if not endorser_conn_id:
raise web.HTTPBadRequest(reason="No endorser connection found")
try:
rev_reg_resp, result = await rev_manager.publish_pending_revocations(
rev_reg_responses, result = await rev_manager.publish_pending_revocations(
rrid2crid=rrid2crid,
write_ledger=write_ledger,
connection_id=endorser_conn_id,
)
except (RevocationError, StorageError, IndyIssuerError, LedgerError) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

if create_transaction_for_endorser and rev_reg_resp:
if create_transaction_for_endorser:
return web.json_response(
(
await _process_publish_response_for_endorsement(
profile, rev_reg_responses, outbound_handler, endorser_conn_id
)
)
)

return web.json_response({"rrid2crid": result})


async def _process_publish_response_for_endorsement(
profile: Profile,
responses: dict,
outbound_handler: BaseResponder.send_outbound,
endorser_conn_id: str,
):
txn_responses = []
for response in responses:
transaction_mgr = TransactionManager(profile)
try:
transaction = await transaction_mgr.create_record(
messages_attach=rev_reg_resp["result"], connection_id=endorser_conn_id
messages_attach=response["result"], connection_id=endorser_conn_id
)
except StorageError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

# if auto-request, send the request to the endorser
if context.settings.get_value("endorser.auto_request"):
if profile.context.settings.get_value("endorser.auto_request"):
try:
(
transaction,
Expand All @@ -683,8 +702,9 @@ async def publish_revocations(request: web.BaseRequest):

await outbound_handler(transaction_request, connection_id=endorser_conn_id)

return web.json_response({"txn": transaction.serialize()})
return web.json_response({"rrid2crid": result})
txn_responses.append({"txn": transaction.serialize()})

return txn_responses


@docs(tags=["revocation"], summary="Clear pending revocations")
Expand Down Expand Up @@ -1906,4 +1926,4 @@ def post_process_routes(app: web.Application):
methods["get"]["responses"]["200"]["schema"] = {
"type": "string",
"format": "binary",
}
}
85 changes: 53 additions & 32 deletions aries_cloudagent/revocation/tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

from ...admin.request_context import AdminRequestContext
from ...askar.profile_anon import AskarAnoncredsProfile
from ...protocols.endorse_transaction.v1_0.manager import TransactionManagerError
from ...storage.error import StorageError
from ...storage.in_memory import InMemoryStorage
from .. import routes as test_module

Expand Down Expand Up @@ -75,9 +77,7 @@ async def test_validate_cred_rev_rec_qs_and_revoke_req(self):
with self.assertRaises(test_module.ValidationError):
req.validate_fields({"rev_reg_id": test_module.INDY_REV_REG_ID_EXAMPLE})
with self.assertRaises(test_module.ValidationError):
req.validate_fields(
{"cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE}
)
req.validate_fields({"cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE})
with self.assertRaises(test_module.ValidationError):
req.validate_fields(
{
Expand Down Expand Up @@ -136,9 +136,7 @@ async def test_revoke_endorser_no_conn_id_by_cred_ex_id(self):
test_module,
"get_endorser_connection_id",
mock.CoroutineMock(return_value="dummy-conn-id"),
), mock.patch.object(
test_module.web, "json_response"
):
), mock.patch.object(test_module.web, "json_response"):
mock_mgr.return_value.revoke_credential = mock.CoroutineMock(
return_value={"result": "..."}
)
Expand Down Expand Up @@ -185,9 +183,7 @@ async def test_revoke_endorser_no_conn_id(self):
test_module,
"get_endorser_connection_id",
mock.CoroutineMock(return_value="dummy-conn-id"),
), mock.patch.object(
test_module.web, "json_response"
):
), mock.patch.object(test_module.web, "json_response"):
mock_mgr.return_value.revoke_credential = mock.CoroutineMock(
return_value={"result": "..."}
)
Expand Down Expand Up @@ -294,9 +290,7 @@ async def test_publish_revocations(self):

await test_module.publish_revocations(self.request)

mock_response.assert_called_once_with(
{"rrid2crid": pub_pending.return_value}
)
mock_response.assert_called_once_with({"rrid2crid": pub_pending.return_value})

async def test_publish_revocations_x(self):
self.request.json = mock.CoroutineMock()
Expand All @@ -319,19 +313,54 @@ async def test_publish_revocations_endorser(self):
test_module,
"get_endorser_connection_id",
mock.CoroutineMock(return_value="dummy-conn-id"),
), mock.patch.object(
test_module.web, "json_response"
) as mock_response:
):
pub_pending = mock.CoroutineMock()
mock_mgr.return_value.publish_pending_revocations = mock.CoroutineMock(
return_value=({}, pub_pending.return_value)
return_value=(
[
{"result": "..."},
{"result": "..."},
],
pub_pending.return_value,
)
)

await test_module.publish_revocations(self.author_request)

mock_response.assert_called_once_with(
{"rrid2crid": pub_pending.return_value}
result = await test_module.publish_revocations(self.author_request)
assert result.status == 200

# Auto endorsement
self.author_request_dict["context"].settings["endorser.auto_request"] = True
self.author_request = mock.MagicMock(
app={},
match_info={},
query={},
__getitem__=lambda _, k: self.author_request_dict[k],
headers={"x-api-key": "author-key"},
)
self.author_request.json = mock.CoroutineMock()
result = await test_module.publish_revocations(self.author_request)
assert result.status == 200

# exceptions
with mock.patch.object(
test_module, "TransactionManager", autospec=True
) as mock_txn_mgr:
mock_txn_mgr.return_value.create_record = mock.CoroutineMock(
side_effect=StorageError()
)
with self.assertRaises(test_module.web.HTTPBadRequest):
result = await test_module.publish_revocations(self.author_request)

with mock.patch.object(
test_module, "TransactionManager", autospec=True
) as mock_txn_mgr:
mock_txn_mgr.return_value.create_request = mock.CoroutineMock(
side_effect=[StorageError(), TransactionManagerError()]
)
with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.publish_revocations(self.author_request)
with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.publish_revocations(self.author_request)

async def test_publish_revocations_endorser_x(self):
self.author_request.json = mock.CoroutineMock()
Expand All @@ -342,9 +371,7 @@ async def test_publish_revocations_endorser_x(self):
test_module,
"get_endorser_connection_id",
mock.CoroutineMock(return_value=None),
), mock.patch.object(
test_module.web, "json_response"
) as mock_response:
), mock.patch.object(test_module.web, "json_response") as mock_response:
pub_pending = mock.CoroutineMock()
mock_mgr.return_value.publish_pending_revocations = pub_pending
with self.assertRaises(test_module.web.HTTPBadRequest):
Expand Down Expand Up @@ -923,9 +950,7 @@ async def test_update_rev_reg(self):
)
self.request.match_info = {"rev_reg_id": REV_REG_ID}
self.request.json = mock.CoroutineMock(
return_value={
"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"
}
return_value={"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"}
)

with mock.patch.object(
Expand Down Expand Up @@ -953,9 +978,7 @@ async def test_update_rev_reg_not_found(self):
)
self.request.match_info = {"rev_reg_id": REV_REG_ID}
self.request.json = mock.CoroutineMock(
return_value={
"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"
}
return_value={"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"}
)

with mock.patch.object(
Expand All @@ -979,9 +1002,7 @@ async def test_update_rev_reg_x(self):
)
self.request.match_info = {"rev_reg_id": REV_REG_ID}
self.request.json = mock.CoroutineMock(
return_value={
"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"
}
return_value={"tails_public_uri": f"http://sample.ca:8181/tails/{REV_REG_ID}"}
)

with mock.patch.object(
Expand Down

0 comments on commit f3a10ba

Please sign in to comment.