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

Fix: RevRegEntry Transaction Endorsement #2558

Merged
merged 21 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
cdf5751
fix + unit tests
shaangill025 Oct 19, 2023
4078b97
Merge branch 'main' into issue_2441
shaangill025 Oct 19, 2023
5d3a5ea
update send_entry on calling publish-revocations
shaangill025 Oct 19, 2023
e14beb2
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 19, 2023
f1b6e26
Merge branch 'issue_2441' of https://github.com/shaangill025/aries-cl…
shaangill025 Oct 19, 2023
e318d31
fix int test
shaangill025 Oct 23, 2023
169372e
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 23, 2023
b79b7f3
Merge branch 'main' into issue_2441
swcurran Nov 2, 2023
cc35120
Merge branch 'main' into issue_2441
swcurran Nov 7, 2023
19ee5f1
Merge branch 'main' into issue_2441
shaangill025 Nov 8, 2023
d8627e4
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 8, 2023
c14710d
Merge branch 'issue_2441' of https://github.com/shaangill025/aries-cl…
shaangill025 Nov 8, 2023
c7c72bb
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 10, 2023
b57e19e
unit test fix
shaangill025 Nov 10, 2023
2a19d12
fix timing issue with revoc verification bdd step
shaangill025 Nov 11, 2023
15fc768
Merge branch 'main' into issue_2441
swcurran Nov 17, 2023
00bd7b1
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 22, 2023
394f117
fixes + fixed tests
shaangill025 Nov 27, 2023
58cce5a
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 27, 2023
4232efa
Merge branch 'main' into issue_2441
dbluhm Nov 28, 2023
3408274
Merge branch 'main' into issue_2441
dbluhm Nov 29, 2023
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
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