Skip to content

Commit

Permalink
Merge pull request #2558 from shaangill025/issue_2441
Browse files Browse the repository at this point in the history
Fix: RevRegEntry Transaction Endorsement
  • Loading branch information
dbluhm authored Nov 29, 2023
2 parents 634adc3 + 3408274 commit cfd9aed
Show file tree
Hide file tree
Showing 7 changed files with 783 additions and 54 deletions.
83 changes: 69 additions & 14 deletions aries_cloudagent/revocation/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import json
import logging
from typing import Mapping, Sequence, Text, Tuple
from typing import Mapping, Optional, Sequence, Text, Tuple

from ..protocols.revocation_notification.v1_0.models.rev_notification_record import (
RevNotificationRecord,
)
from ..connections.models.conn_record import ConnRecord
from ..core.error import BaseError
from ..core.profile import Profile
from ..indy.issuer import IndyIssuer
Expand Down Expand Up @@ -47,7 +48,9 @@ async def revoke_credential_by_cred_ex_id(
notify_version: str = None,
thread_id: str = None,
connection_id: str = None,
endorser_conn_id: str = None,
comment: str = None,
write_ledger: bool = True,
):
"""Revoke a credential by its credential exchange identifier at issue.
Expand Down Expand Up @@ -79,7 +82,9 @@ async def revoke_credential_by_cred_ex_id(
notify_version=notify_version,
thread_id=thread_id,
connection_id=connection_id,
endorser_conn_id=endorser_conn_id,
comment=comment,
write_ledger=write_ledger,
)

async def revoke_credential(
Expand All @@ -91,8 +96,10 @@ async def revoke_credential(
notify_version: str = None,
thread_id: str = None,
connection_id: str = None,
endorser_conn_id: str = None,
comment: str = None,
):
write_ledger: bool = True,
) -> Optional[dict]:
"""Revoke a credential.
Optionally, publish the corresponding revocation registry delta to the ledger.
Expand Down Expand Up @@ -147,15 +154,38 @@ async def revoke_credential(
await txn.commit()
await self.set_cred_revoked_state(rev_reg_id, crids)
if delta_json:
await issuer_rr_upd.send_entry(self._profile)
await notify_revocation_published_event(
self._profile, rev_reg_id, [cred_rev_id]
)

if write_ledger:
rev_entry_resp = await issuer_rr_upd.send_entry(self._profile)
await notify_revocation_published_event(
self._profile, rev_reg_id, [cred_rev_id]
)
return rev_entry_resp
else:
async with self._profile.session() as session:
try:
connection_record = await ConnRecord.retrieve_by_id(
session, endorser_conn_id
)
except StorageNotFoundError:
raise RevocationManagerError(
"No endorser connection record found "
f"for id: {endorser_conn_id}"
)
endorser_info = await connection_record.metadata_get(
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,
)
return rev_entry_resp
else:
async with self._profile.transaction() as txn:
await issuer_rr_rec.mark_pending(txn, cred_rev_id)
await txn.commit()
return None

async def update_rev_reg_revoked_state(
self,
Expand All @@ -182,7 +212,9 @@ async def update_rev_reg_revoked_state(
async def publish_pending_revocations(
self,
rrid2crid: Mapping[Text, Sequence[Text]] = None,
) -> Mapping[Text, Sequence[Text]]:
write_ledger: bool = True,
connection_id: str = None,
) -> Tuple[Optional[dict], Mapping[Text, Sequence[Text]]]:
"""Publish pending revocations to the ledger.
Args:
Expand All @@ -202,12 +234,13 @@ async def publish_pending_revocations(
- all pending revocations from all revocation registry tagged 0
- pending ["1", "2"] from revocation registry tagged 1
- no pending revocations from any other revocation registries.
connection_id: connection identifier for endorser connection to use
Returns: mapping from each revocation registry id to its cred rev ids published.
"""
result = {}
issuer = self._profile.inject(IndyIssuer)

rev_entry_resp = None
async with self._profile.session() as session:
issuer_rr_recs = await IssuerRevRegRecord.query_by_pending(session)

Expand Down Expand Up @@ -239,14 +272,36 @@ async def publish_pending_revocations(
await txn.commit()
await self.set_cred_revoked_state(issuer_rr_rec.revoc_reg_id, crids)
if delta_json:
await issuer_rr_upd.send_entry(self._profile)
if connection_id:
async with self._profile.session() as session:
try:
connection_record = await ConnRecord.retrieve_by_id(
session, connection_id
)
except StorageNotFoundError:
raise RevocationManagerError(
"No endorser connection record found "
f"for id: {connection_id}"
)
endorser_info = await connection_record.metadata_get(
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,
)
else:
rev_entry_resp = await issuer_rr_upd.send_entry(self._profile)
published = sorted(crid for crid in crids if crid not in failed_crids)
result[issuer_rr_rec.revoc_reg_id] = published
await notify_revocation_published_event(
self._profile, issuer_rr_rec.revoc_reg_id, crids
)
if not connection_id:
await notify_revocation_published_event(
self._profile, issuer_rr_rec.revoc_reg_id, crids
)

return result
return rev_entry_resp, result

async def clear_pending_revocations(
self, purge: Mapping[Text, Sequence[Text]] = None
Expand Down
121 changes: 109 additions & 12 deletions aries_cloudagent/revocation/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,8 @@ class RevRegConnIdMatchInfoSchema(OpenAPISchema):
summary="Revoke an issued credential",
)
@request_schema(RevokeRequestSchema())
@querystring_schema(CreateRevRegTxnForEndorserOptionSchema())
@querystring_schema(RevRegConnIdMatchInfoSchema())
@response_schema(RevocationModuleResponseSchema(), description="")
async def revoke(request: web.BaseRequest):
"""Request handler for storing a credential revocation.
Expand All @@ -519,27 +521,58 @@ async def revoke(request: web.BaseRequest):
body = await request.json()
cred_ex_id = body.get("cred_ex_id")
body["notify"] = body.get("notify", context.settings.get("revocation.notify"))
notify = body.get("notify")
notify = body.get("notify", False)
connection_id = body.get("connection_id")
body["notify_version"] = body.get("notify_version", "v1_0")
notify_version = body["notify_version"]
create_transaction_for_endorser = json.loads(
request.query.get("create_transaction_for_endorser", "false")
)
endorser_conn_id = request.query.get("conn_id")
rev_manager = RevocationManager(context.profile)
profile = context.profile
outbound_handler = request["outbound_message_router"]
write_ledger = not create_transaction_for_endorser

if is_author_role(profile):
write_ledger = False
create_transaction_for_endorser = True
if not endorser_conn_id:
endorser_conn_id = await get_endorser_connection_id(profile)
if not endorser_conn_id:
raise web.HTTPBadRequest(reason="No endorser connection found")
if notify and not connection_id:
raise web.HTTPBadRequest(reason="connection_id must be set when notify is true")
if notify and not notify_version:
raise web.HTTPBadRequest(
reason="Request must specify notify_version if notify is true"
)

rev_manager = RevocationManager(context.profile)
try:
if cred_ex_id:
# rev_reg_id and cred_rev_id should not be present so we can
# safely splat the body
await rev_manager.revoke_credential_by_cred_ex_id(**body)
rev_entry_resp = await rev_manager.revoke_credential_by_cred_ex_id(
cred_ex_id=cred_ex_id,
publish=body.get("publish", False),
notify=notify,
notify_version=notify_version,
thread_id=body.get("thread_id"),
connection_id=connection_id,
endorser_conn_id=endorser_conn_id,
comment=body.get("comment"),
write_ledger=write_ledger,
)
else:
# no cred_ex_id so we can safely splat the body
await rev_manager.revoke_credential(**body)
rev_entry_resp = await rev_manager.revoke_credential(
rev_reg_id=body.get("rev_reg_id"),
cred_rev_id=body.get("cred_rev_id"),
publish=body.get("publish", False),
notify=notify,
notify_version=notify_version,
thread_id=body.get("thread_id"),
connection_id=connection_id,
endorser_conn_id=endorser_conn_id,
comment=body.get("comment"),
write_ledger=write_ledger,
)
except (
RevocationManagerError,
RevocationError,
Expand All @@ -549,11 +582,37 @@ async def revoke(request: web.BaseRequest):
) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

if create_transaction_for_endorser and rev_entry_resp:
transaction_mgr = TransactionManager(profile)
try:
transaction = await transaction_mgr.create_record(
messages_attach=rev_entry_resp["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"):
try:
(
transaction,
transaction_request,
) = await transaction_mgr.create_request(
transaction=transaction,
)
except (StorageError, TransactionManagerError) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

await outbound_handler(transaction_request, connection_id=endorser_conn_id)

return web.json_response({"txn": transaction.serialize()})
return web.json_response({})


@docs(tags=["revocation"], summary="Publish pending revocations to ledger")
@request_schema(PublishRevocationsSchema())
@querystring_schema(CreateRevRegTxnForEndorserOptionSchema())
@querystring_schema(RevRegConnIdMatchInfoSchema())
@response_schema(TxnOrPublishRevocationsResultSchema(), 200, description="")
async def publish_revocations(request: web.BaseRequest):
"""Request handler for publishing pending revocations to the ledger.
Expand All @@ -568,17 +627,55 @@ async def publish_revocations(request: web.BaseRequest):
context: AdminRequestContext = request["context"]
body = await request.json()
rrid2crid = body.get("rrid2crid")

create_transaction_for_endorser = json.loads(
request.query.get("create_transaction_for_endorser", "false")
)
write_ledger = not create_transaction_for_endorser
endorser_conn_id = request.query.get("conn_id")
rev_manager = RevocationManager(context.profile)
profile = context.profile
outbound_handler = request["outbound_message_router"]

if is_author_role(profile):
write_ledger = False
create_transaction_for_endorser = True
endorser_conn_id = await get_endorser_connection_id(profile)
if not endorser_conn_id:
raise web.HTTPBadRequest(reason="No endorser connection found")
try:
rev_reg_resp = await rev_manager.publish_pending_revocations(
rrid2crid,
rev_reg_resp, 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

return web.json_response({"rrid2crid": rev_reg_resp})
if create_transaction_for_endorser and rev_reg_resp:
transaction_mgr = TransactionManager(profile)
try:
transaction = await transaction_mgr.create_record(
messages_attach=rev_reg_resp["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"):
try:
(
transaction,
transaction_request,
) = await transaction_mgr.create_request(
transaction=transaction,
)
except (StorageError, TransactionManagerError) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

await outbound_handler(transaction_request, connection_id=endorser_conn_id)

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


@docs(tags=["revocation"], summary="Clear pending revocations")
Expand Down
Loading

0 comments on commit cfd9aed

Please sign in to comment.