Skip to content

Commit

Permalink
Merge pull request #1601 from ianco/endorser-did-support
Browse files Browse the repository at this point in the history
DID updates for endorser
  • Loading branch information
ianco authored Jan 20, 2022
2 parents fecb0a8 + 010d674 commit 919fdfc
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 20 deletions.
10 changes: 8 additions & 2 deletions aries_cloudagent/ledger/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,14 @@ async def update_endpoint_for_did(

@abstractmethod
async def register_nym(
self, did: str, verkey: str, alias: str = None, role: str = None
):
self,
did: str,
verkey: str,
alias: str = None,
role: str = None,
write_ledger: bool = True,
endorser_did: str = None,
) -> Tuple[bool, dict]:
"""
Register a nym on the ledger.
Expand Down
22 changes: 17 additions & 5 deletions aries_cloudagent/ledger/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,8 +928,14 @@ async def update_endpoint_for_did(
return False

async def register_nym(
self, did: str, verkey: str, alias: str = None, role: str = None
):
self,
did: str,
verkey: str,
alias: str = None,
role: str = None,
write_ledger: bool = True,
endorser_did: str = None,
) -> Tuple[bool, dict]:
"""
Register a nym on the ledger.
Expand Down Expand Up @@ -957,17 +963,23 @@ async def register_nym(
request_json = await indy.ledger.build_nym_request(
public_info.did, did, verkey, alias, role
)
await self._submit(
request_json
if endorser_did and not write_ledger:
request_json = await indy.ledger.append_request_endorser(
request_json, endorser_did
)
resp = await self._submit(
request_json, sign=True, sign_did=public_info, write_ledger=write_ledger
) # let ledger raise on insufficient privilege

if not write_ledger:
return True, {"signed_txn": resp}
try:
did_info = await wallet.get_local_did(did)
except WalletNotFoundError:
pass # registering another user's NYM
else:
metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata}
await wallet.replace_local_did_metadata(did, metadata)
return True, None

async def get_nym_role(self, did: str) -> Role:
"""
Expand Down
19 changes: 16 additions & 3 deletions aries_cloudagent/ledger/indy_vdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,8 +938,14 @@ async def update_endpoint_for_did(
return False

async def register_nym(
self, did: str, verkey: str, alias: str = None, role: str = None
):
self,
did: str,
verkey: str,
alias: str = None,
role: str = None,
write_ledger: bool = True,
endorser_did: str = None,
) -> Tuple[bool, dict]:
"""
Register a nym on the ledger.
Expand All @@ -965,8 +971,14 @@ async def register_nym(
except VdrError as err:
raise LedgerError("Exception when building nym request") from err

await self._submit(nym_req, sign=True, sign_did=public_info)
if endorser_did and not write_ledger:
nym_req.set_endorser(endorser_did)

resp = await self._submit(
nym_req, sign=True, sign_did=public_info, write_ledger=write_ledger
)
if not write_ledger:
return True, {"signed_txn": resp}
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
try:
Expand All @@ -976,6 +988,7 @@ async def register_nym(
else:
metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata}
await wallet.replace_local_did_metadata(did, metadata)
return True, None

async def get_nym_role(self, did: str) -> Role:
"""
Expand Down
138 changes: 132 additions & 6 deletions aries_cloudagent/ledger/routes.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
"""Ledger admin routes."""

import json

from aiohttp import web
from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema
from marshmallow import fields, validate

from ..admin.request_context import AdminRequestContext
from ..connections.models.conn_record import ConnRecord
from ..messaging.models.base import BaseModelError
from ..messaging.models.openapi import OpenAPISchema
from ..messaging.valid import (
ENDPOINT,
ENDPOINT_TYPE,
INDY_DID,
INDY_RAW_PUBLIC_KEY,
INT_EPOCH,
UUIDFour,
)

from ..protocols.endorse_transaction.v1_0.manager import (
TransactionManager,
TransactionManagerError,
)
from ..protocols.endorse_transaction.v1_0.models.transaction_record import (
TransactionRecordSchema,
)
from ..protocols.endorse_transaction.v1_0.util import (
is_author_role,
get_endorser_connection_id,
)
from ..storage.error import StorageError
from ..storage.error import StorageError, StorageNotFoundError
from ..wallet.error import WalletError, WalletNotFoundError

from .base import BaseLedger, Role as LedgerRole
Expand All @@ -32,6 +49,7 @@
)
from .endpoint_type import EndpointType
from .error import BadLedgerRequestError, LedgerError, LedgerTransactionError
from .util import notify_did_event


class LedgerModulesResultSchema(OpenAPISchema):
Expand Down Expand Up @@ -109,6 +127,23 @@ class RegisterLedgerNymQueryStringSchema(OpenAPISchema):
)


class CreateDidTxnForEndorserOptionSchema(OpenAPISchema):
"""Class for user to input whether to create a transaction for endorser or not."""

create_transaction_for_endorser = fields.Boolean(
description="Create Transaction For Endorser's signature",
required=False,
)


class SchemaConnIdMatchInfoSchema(OpenAPISchema):
"""Path parameters and validators for request taking connection id."""

conn_id = fields.Str(
description="Connection identifier", required=False, example=UUIDFour.EXAMPLE
)


class QueryStringDIDSchema(OpenAPISchema):
"""Parameters and validators for query string with DID only."""

Expand All @@ -128,14 +163,20 @@ class QueryStringEndpointSchema(OpenAPISchema):
)


class RegisterLedgerNymResponseSchema(OpenAPISchema):
class TxnOrRegisterLedgerNymResponseSchema(OpenAPISchema):
"""Response schema for ledger nym registration."""

success = fields.Bool(
description="Success of nym registration operation",
example=True,
)

txn = fields.Nested(
TransactionRecordSchema(),
required=False,
description="DID transaction to endorse",
)


class GetNymRoleResponseSchema(OpenAPISchema):
"""Response schema to get nym role operation."""
Expand Down Expand Up @@ -172,7 +213,9 @@ class GetDIDEndpointResponseSchema(OpenAPISchema):
summary="Send a NYM registration to the ledger.",
)
@querystring_schema(RegisterLedgerNymQueryStringSchema())
@response_schema(RegisterLedgerNymResponseSchema(), 200, description="")
@querystring_schema(CreateDidTxnForEndorserOptionSchema())
@querystring_schema(SchemaConnIdMatchInfoSchema())
@response_schema(TxnOrRegisterLedgerNymResponseSchema(), 200, description="")
async def register_ledger_nym(request: web.BaseRequest):
"""
Request handler for registering a NYM with the ledger.
Expand All @@ -181,6 +224,7 @@ async def register_ledger_nym(request: web.BaseRequest):
request: aiohttp request object
"""
context: AdminRequestContext = request["context"]
outbound_handler = request["outbound_message_router"]
async with context.profile.session() as session:
ledger = session.inject_or(BaseLedger)
if not ledger:
Expand All @@ -201,11 +245,63 @@ async def register_ledger_nym(request: web.BaseRequest):
if role == "reset": # indy: empty to reset, null for regular user
role = "" # visually: confusing - correct 'reset' to empty string here

create_transaction_for_endorser = json.loads(
request.query.get("create_transaction_for_endorser", "false")
)
write_ledger = not create_transaction_for_endorser
endorser_did = None
connection_id = request.query.get("conn_id")

# check if we need to endorse
if is_author_role(context.profile):
# authors cannot write to the ledger
write_ledger = False
create_transaction_for_endorser = True
if not connection_id:
# author has not provided a connection id, so determine which to use
connection_id = await get_endorser_connection_id(context.profile)
if not connection_id:
raise web.HTTPBadRequest(reason="No endorser connection found")

if not write_ledger:
try:
async with context.profile.session() as session:
connection_record = await ConnRecord.retrieve_by_id(
session, connection_id
)
except StorageNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err
except BaseModelError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

async with context.profile.session() as session:
endorser_info = await connection_record.metadata_get(
session, "endorser_info"
)
if not endorser_info:
raise web.HTTPForbidden(
reason="Endorser Info is not set up in "
"connection metadata for this connection record"
)
if "endorser_did" not in endorser_info.keys():
raise web.HTTPForbidden(
reason=' "endorser_did" is not set in "endorser_info"'
" in connection metadata for this connection record"
)
endorser_did = endorser_info["endorser_did"]

success = False
txn = None
async with ledger:
try:
await ledger.register_nym(did, verkey, alias, role)
success = True
(success, txn) = await ledger.register_nym(
did,
verkey,
alias,
role,
write_ledger=write_ledger,
endorser_did=endorser_did,
)
except LedgerTransactionError as err:
raise web.HTTPForbidden(reason=err.roll_up)
except LedgerError as err:
Expand All @@ -220,7 +316,37 @@ async def register_ledger_nym(request: web.BaseRequest):
)
)

return web.json_response({"success": success})
meta_data = {"verkey": verkey, "alias": alias, "role": role}
if not create_transaction_for_endorser:
# Notify event
await notify_did_event(context.profile, did, meta_data)
return web.json_response({"success": success})
else:
transaction_mgr = TransactionManager(context.profile)
try:
transaction = await transaction_mgr.create_record(
messages_attach=txn["signed_txn"],
connection_id=connection_id,
meta_data=meta_data,
)
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,
# TODO see if we need to parameterize these params
# expires_time=expires_time,
# endorser_write_txn=endorser_write_txn,
)
except (StorageError, TransactionManagerError) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

await outbound_handler(transaction_request, connection_id=connection_id)

return web.json_response({"success": success, "txn": txn})


@docs(
Expand Down
Loading

0 comments on commit 919fdfc

Please sign in to comment.