Skip to content

Commit

Permalink
allow auto-remove of credential exchange records to be disabled; trac…
Browse files Browse the repository at this point in the history
…k credential revocation registry and ID; update performance demo

Signed-off-by: Andrew Whitehead <[email protected]>
  • Loading branch information
andrewwhitehead committed Dec 13, 2019
1 parent 7597cc7 commit c3c1010
Show file tree
Hide file tree
Showing 14 changed files with 660 additions and 432 deletions.
1 change: 1 addition & 0 deletions aries_cloudagent/config/default_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ async def load_plugins(self, context: InjectionContext):
"aries_cloudagent.messaging.credential_definitions"
)
plugin_registry.register_plugin("aries_cloudagent.messaging.schemas")
plugin_registry.register_plugin("aries_cloudagent.revocation")
plugin_registry.register_plugin("aries_cloudagent.wallet")

# Register external plugins
Expand Down
1 change: 1 addition & 0 deletions aries_cloudagent/ledger/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ async def create_and_send_credential_definition(
)
storage = self.get_indy_storage()
await storage.add_record(record)
print(record.tags)

return credential_definition_id, json.loads(credential_definition_json)

Expand Down
18 changes: 0 additions & 18 deletions aries_cloudagent/messaging/credential_definitions/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,6 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq
)
)

# if support_revocation:
# # TODO - move into scheduled task?
# issuer_did = credential_definition_id.split(":")[0]
# IndyRevocation = ClassLoader.load_class(
# "aries_cloudagent.revocation.indy.IndyRevocation"
# )
# revoc = IndyRevocation(context)
# registry_record = await revoc.init_issuer_registry(
# credential_definition_id, issuer_did
# )
# await registry_record.generate_registry(context, revoc.get_temp_dir())
# print("generated tails file")
# # print(registry_record)
# await registry_record.publish_registry_definition(context)
# await registry_record.publish_registry_entry(context)
# print("published registry definition")
# print("published registry entry")

return web.json_response({"credential_definition_id": credential_definition_id})


Expand Down
62 changes: 55 additions & 7 deletions aries_cloudagent/protocols/issue_credential/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ async def _match_sent_cred_def_id(self, tag_query: Mapping[str, str]) -> str:
return max(found, key=lambda r: int(r.tags["epoch"])).tags["cred_def_id"]

async def prepare_send(
self, connection_id: str, credential_proposal: CredentialProposal
self,
connection_id: str,
credential_proposal: CredentialProposal,
revoc_reg_id: str = None,
auto_remove: bool = True,
) -> Tuple[V10CredentialExchange, CredentialOffer]:
"""
Set up a new credential exchange for an automated send.
Expand All @@ -77,17 +81,21 @@ async def prepare_send(
connection_id: Connection to create offer for
credential_proposal: The credential proposal with preview on
attribute values to use if auto_issue is enabled
revoc_reg_id: ID of the revocation registry to use
auto_remove: Flag to automatically remove the record on completion
Returns:
A tuple of the new credential exchange record and credential offer message
"""
credential_exchange = V10CredentialExchange(
auto_issue=True,
auto_remove=auto_remove,
connection_id=connection_id,
initiator=V10CredentialExchange.INITIATOR_SELF,
role=V10CredentialExchange.ROLE_ISSUER,
credential_proposal_dict=credential_proposal.serialize(),
revoc_reg_id=revoc_reg_id,
)
(credential_exchange, credential_offer) = await self.create_offer(
credential_exchange_record=credential_exchange,
Expand All @@ -100,6 +108,7 @@ async def create_proposal(
connection_id: str,
*,
auto_offer: bool = None,
auto_remove: bool = True,
comment: str = None,
credential_preview: CredentialPreview = None,
schema_id: str = None,
Expand All @@ -108,6 +117,7 @@ async def create_proposal(
schema_version: str = None,
cred_def_id: str = None,
issuer_did: str = None,
revoc_reg_id: str = None,
) -> V10CredentialExchange:
"""
Create a credential proposal.
Expand All @@ -116,6 +126,7 @@ async def create_proposal(
connection_id: Connection to create proposal for
auto_offer: Should this proposal request automatically be handled to
offer a credential
auto_remove: Should the record be automatically removed on completion
comment: Optional human-readable comment to include in proposal
credential_preview: The credential preview to use to create
the credential proposal
Expand All @@ -125,6 +136,7 @@ async def create_proposal(
schema_version: Schema version for credential proposal
cred_def_id: Credential definition id for credential proposal
issuer_did: Issuer DID for credential proposal
revoc_reg_id: ID of the revocation registry to use
Returns:
Resulting credential exchange record including credential proposal
Expand Down Expand Up @@ -153,6 +165,8 @@ async def create_proposal(
state=V10CredentialExchange.STATE_PROPOSAL_SENT,
credential_proposal_dict=credential_proposal_message.serialize(),
auto_offer=auto_offer,
auto_remove=auto_remove,
revoc_reg_id=revoc_reg_id,
)
await credential_exchange_record.save(
self.context, reason="create credential proposal"
Expand Down Expand Up @@ -224,6 +238,19 @@ async def create_offer(
cred_preview = None

async def _create(cred_def_id):
ledger: BaseLedger = await self.context.inject(BaseLedger)
async with ledger:
credential_definition = await ledger.get_credential_definition(
cred_def_id
)
if (
credential_definition["value"].get("revocation")
and not credential_exchange_record.revoc_reg_id
):
raise CredentialManagerError(
"Missing revocation registry ID for revocable credential definition"
)

issuer: BaseIssuer = await self.context.inject(BaseIssuer)
return await issuer.create_credential_offer(cred_def_id)

Expand Down Expand Up @@ -464,13 +491,31 @@ async def issue_credential(
async with ledger:
schema = await ledger.get_schema(schema_id)

if credential_exchange_record.revoc_reg_id:
revoc = IndyRevocation(self.context)
registry_record = await revoc.get_issuer_revocation_record(
credential_exchange_record.revoc_reg_id
)
# FIXME exception on missing

registry = await registry_record.get_registry()
tails_reader = await registry.create_tails_reader()
else:
tails_reader = None

issuer: BaseIssuer = await self.context.inject(BaseIssuer)
(
credential_exchange_record.credential,
_, # credential_revocation_id
credential_exchange_record.revocation_id,
) = await issuer.create_credential(
schema, credential_offer, credential_request, credential_values
schema,
credential_offer,
credential_request,
credential_values,
credential_exchange_record.revoc_reg_id,
tails_reader,
)
# FIXME - exception if registry is full?

credential_exchange_record.state = V10CredentialExchange.STATE_ISSUED
await credential_exchange_record.save(self.context, reason="issue credential")
Expand Down Expand Up @@ -564,8 +609,10 @@ async def store_credential(
credential_exchange_record.parent_thread_id,
)

# Delete the exchange record since we're done with it
await credential_exchange_record.delete_record(self.context)
if credential_exchange_record.auto_remove:
# Delete the exchange record since we're done with it
await credential_exchange_record.delete_record(self.context)

return (credential_exchange_record, credential_ack_message)

async def receive_credential_ack(self) -> V10CredentialExchange:
Expand All @@ -588,8 +635,9 @@ async def receive_credential_ack(self) -> V10CredentialExchange:
credential_exchange_record.state = V10CredentialExchange.STATE_ACKED
await credential_exchange_record.save(self.context, reason="credential acked")

# We're done with the exchange so delete
await credential_exchange_record.delete_record(self.context)
if credential_exchange_record.auto_remove:
# We're done with the exchange so delete
await credential_exchange_record.delete_record(self.context)

return credential_exchange_record

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(
revocation_id: str = None,
auto_offer: bool = False,
auto_issue: bool = False,
auto_remove: bool = True,
error_msg: str = None,
**kwargs,
):
Expand All @@ -85,6 +86,7 @@ def __init__(
self.revocation_id = revocation_id
self.auto_offer = auto_offer
self.auto_issue = auto_issue
self.auto_remove = auto_remove
self.error_msg = error_msg

@property
Expand All @@ -106,6 +108,7 @@ def record_value(self) -> dict:
"error_msg",
"auto_offer",
"auto_issue",
"auto_remove",
"raw_credential",
"credential",
"parent_thread_id",
Expand Down Expand Up @@ -214,6 +217,14 @@ class Meta:
description="Issuer choice to issue to request in this credential exchange",
example=False,
)
auto_remove = fields.Bool(
required=False,
default=True,
description=(
"Issuer choice to remove this credential exchange record when complete"
),
example=False,
)
error_msg = fields.Str(
required=False,
description="Error message",
Expand Down
56 changes: 38 additions & 18 deletions aries_cloudagent/protocols/issue_credential/v1_0/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ....messaging.valid import (
INDY_CRED_DEF_ID,
INDY_DID,
INDY_REV_REG_ID,
INDY_SCHEMA_ID,
INDY_VERSION,
UUIDFour,
Expand Down Expand Up @@ -57,29 +58,27 @@ class V10CredentialProposalRequestSchema(Schema):
**INDY_CRED_DEF_ID,
)
schema_id = fields.Str(
description="Schema identifier",
required=False,
**INDY_SCHEMA_ID,
description="Schema identifier", required=False, **INDY_SCHEMA_ID,
)
schema_issuer_did = fields.Str(
description="Schema issuer DID",
required=False,
**INDY_DID,
description="Schema issuer DID", required=False, **INDY_DID,
)
schema_name = fields.Str(
description="Schema name",
required=False,
example="preferences",
description="Schema name", required=False, example="preferences",
)
schema_version = fields.Str(
description="Schema version",
required=False,
**INDY_VERSION,
description="Schema version", required=False, **INDY_VERSION,
)
issuer_did = fields.Str(
description="Credential issuer DID",
description="Credential issuer DID", required=False, **INDY_DID,
)
auto_remove = fields.Bool(
description=("Whether to remove the credential exchange record on completion"),
required=False,
**INDY_DID,
default=True,
)
revoc_reg_id = fields.Str(
description="Revocation Registry ID", required=False, **INDY_REV_REG_ID,
)
comment = fields.Str(description="Human-readable comment", required=False)
credential_proposal = fields.Nested(CredentialPreviewSchema, required=True)
Expand All @@ -106,6 +105,14 @@ class V10CredentialOfferRequestSchema(Schema):
required=False,
default=False,
)
auto_remove = fields.Bool(
description=("Whether to remove the credential exchange record on completion"),
required=False,
default=True,
)
revoc_reg_id = fields.Str(
description="Revocation Registry ID", required=False, **INDY_REV_REG_ID,
)
comment = fields.Str(description="Human-readable comment", required=False)
credential_preview = fields.Nested(CredentialPreviewSchema, required=True)

Expand Down Expand Up @@ -220,6 +227,8 @@ async def credential_exchange_send(request: web.BaseRequest):
preview_spec = body.get("credential_proposal")
if not preview_spec:
raise web.HTTPBadRequest(reason="credential_proposal must be provided.")
auto_remove = body.get("auto_remove", True)
revoc_reg_id = body.get("revoc_reg_id")

try:
connection_record = await ConnectionRecord.retrieve_by_id(
Expand All @@ -243,7 +252,10 @@ async def credential_exchange_send(request: web.BaseRequest):
credential_exchange_record,
credential_offer_message,
) = await credential_manager.prepare_send(
connection_id, credential_proposal=credential_proposal
connection_id,
credential_proposal=credential_proposal,
auto_remove=auto_remove,
revoc_reg_id=revoc_reg_id,
)
await outbound_handler(
credential_offer_message, connection_id=credential_exchange_record.connection_id
Expand Down Expand Up @@ -276,6 +288,8 @@ async def credential_exchange_send_proposal(request: web.BaseRequest):
preview_spec = body.get("credential_proposal")
if not preview_spec:
raise web.HTTPBadRequest(reason="credential_proposal must be provided.")
auto_remove = body.get("auto_remove", True)
revoc_reg_id = body.get("revoc_reg_id")

try:
connection_record = await ConnectionRecord.retrieve_by_id(
Expand All @@ -295,6 +309,8 @@ async def credential_exchange_send_proposal(request: web.BaseRequest):
connection_id,
comment=comment,
credential_preview=credential_preview,
auto_remove=auto_remove,
revoc_reg_id=revoc_reg_id,
**{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)},
)

Expand Down Expand Up @@ -339,6 +355,8 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest):
auto_issue = body.get(
"auto_issue", context.settings.get("debug.auto_respond_credential_request")
)
auto_remove = body.get("auto_remove", True)
revoc_reg_id = body.get("revoc_reg_id")
comment = body.get("comment")
preview_spec = body.get("credential_preview")

Expand Down Expand Up @@ -378,6 +396,8 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest):
credential_definition_id=cred_def_id,
credential_proposal_dict=credential_proposal_dict,
auto_issue=auto_issue,
auto_remove=auto_remove,
revoc_reg_id=revoc_reg_id,
)

credential_manager = CredentialManager(context)
Expand Down Expand Up @@ -659,7 +679,8 @@ async def credential_exchange_revoke(request: web.BaseRequest):
)

if (
credential_exchange_record.state != V10CredentialExchange.STATE_ISSUED
credential_exchange_record.state
not in (V10CredentialExchange.STATE_ISSUED, V10CredentialExchange.STATE_ACKED)
or not credential_exchange_record.revocation_id
or not credential_exchange_record.revoc_reg_id
):
Expand Down Expand Up @@ -731,8 +752,7 @@ async def register(app: web.Application):
credential_exchange_store,
),
web.post(
"/issue-credential/{id}/revoke",
credential_exchange_revoke
"/issue-credential/records/{id}/revoke", credential_exchange_revoke
),
web.post(
"/issue-credential/records/{cred_ex_id}/problem-report",
Expand Down
11 changes: 11 additions & 0 deletions aries_cloudagent/revocation/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Revocation error classes."""

from ..core.error import BaseError


class RevocationError(BaseError):
"""Base exception for revocation-related errors."""


class RevocationNotSupportedError(RevocationError):
"""Attempted to create registry for non-revocable cred def."""
Loading

0 comments on commit c3c1010

Please sign in to comment.