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

Updates to invitation handling and performance test #68

Merged
merged 8 commits into from
Jul 15, 2019
1 change: 0 additions & 1 deletion DevReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ Most configuration parameters are provided to the the agent at startup. Refer to
| `--no-receive-invites` | `--no-receive-invites` | Disables the receive invitations administration function. | `false` |
| `--help-link` | `--help-link` | Defines the help URL for the administration interface. | `false` |
| `--invite` | `--invite` | Generates and print a new connection invitation URL on start-up. | `false` |
| `--send-invite` | `--send-invite` | Specifies an endpoint to send an invitation to on start-up. | `false` |
| `--timing` | `--timing` | Includes timing information in response messages. | `false` |
| `--protocol` | `--protocol` | Instructs the agent to load an external protocol module. | `false` |
| `--webhook-url` | `--webhook-url` | Instructs the agent to send webhooks containing internal state changes to a URL. This is useful for a controller to monitor changes and prompt new behaviour using the admin API. | `false` |
Expand Down
12 changes: 1 addition & 11 deletions aries_cloudagent/conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,17 +214,7 @@ async def start(self) -> None:
print("Invitation URL:")
print(invite_url)
except Exception:
self.logger.exception("Error sending invitation")

# Auto-send an invitation to another agent
send_invite_to = context.settings.get("debug.send_invitation_to")
if send_invite_to:
try:
mgr = ConnectionManager(self.context)
_connection, invitation = await mgr.create_invitation()
await mgr.send_invitation(invitation, send_invite_to)
except Exception:
self.logger.exception("Error sending invitation")
self.logger.exception("Error creating invitation")

async def stop(self, timeout=0.1):
"""Stop the agent."""
Expand Down
9 changes: 0 additions & 9 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,6 @@
help="Generate and print a new connection invitation URL",
)

PARSER.add_argument(
"--send-invite",
type=str,
metavar="<agent-endpoint>",
help="Specify an endpoint to send an invitation to",
)

PARSER.add_argument(
"--timing",
action="store_true",
Expand Down Expand Up @@ -325,8 +318,6 @@ def get_settings(args):
settings["debug.seed"] = args.debug_seed
if args.invite:
settings["debug.print_invitation"] = True
if args.send_invite:
settings["debug.send_invitation_to"] = args.send_invite

if args.auto_respond_credential_offer:
settings["auto_respond_credential_offer"] = True
Expand Down
14 changes: 11 additions & 3 deletions aries_cloudagent/ledger/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
from ..error import BaseError


class ClosedPoolError(BaseError):
class LedgerError(BaseError):
"""Base class for ledger errors."""


class BadLedgerRequestError(LedgerError):
"""The current request cannot proceed."""


class ClosedPoolError(LedgerError):
"""Indy pool is closed."""


class LedgerTransactionError(BaseError):
class LedgerTransactionError(LedgerError):
"""The ledger rejected the transaction."""


class DuplicateSchemaError(BaseError):
class DuplicateSchemaError(LedgerError):
"""The schema already exists on the ledger."""
32 changes: 21 additions & 11 deletions aries_cloudagent/ledger/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
from ..wallet.base import BaseWallet

from .base import BaseLedger
from .error import ClosedPoolError, LedgerTransactionError, DuplicateSchemaError
from .error import (
BadLedgerRequestError,
ClosedPoolError,
LedgerTransactionError,
DuplicateSchemaError,
)

GENESIS_TRANSACTION_PATH = tempfile.gettempdir()
GENESIS_TRANSACTION_PATH = path.join(
Expand Down Expand Up @@ -160,9 +165,10 @@ async def _submit(self, request_json: str, sign=True) -> str:
"Cannot sign and submit request to closed pool {}".format(self.name)
)

public_did = await self.wallet.get_public_did()

if sign:
public_did = await self.wallet.get_public_did()
if not public_did:
raise BadLedgerRequestError("Cannot sign request without a public DID")
request_result_json = await indy.ledger.sign_and_submit_request(
self.pool_handle, self.wallet.handle, public_did.did, request_json
)
Expand All @@ -177,10 +183,7 @@ async def _submit(self, request_json: str, sign=True) -> str:

# HACK: If only there were a better way to identify this kind
# of rejected request...
if (
"can have one and only one SCHEMA with name"
in request_result_json
):
if "can have one and only one SCHEMA with name" in request_result_json:
raise DuplicateSchemaError()

if operation in ("REQNACK", "REJECT"):
Expand Down Expand Up @@ -210,6 +213,8 @@ async def send_schema(
"""

public_did = await self.wallet.get_public_did()
if not public_did:
raise BadLedgerRequestError("Cannot publish schema without a public DID")

schema_id, schema_json = await indy.anoncreds.issuer_create_schema(
public_did.did, schema_name, schema_version, json.dumps(attribute_names)
Expand Down Expand Up @@ -255,10 +260,10 @@ async def fetch_schema(self, schema_id: str):
public_did = await self.wallet.get_public_did()

request_json = await indy.ledger.build_get_schema_request(
public_did.did, schema_id
public_did.did if public_did else None, schema_id
)

response_json = await self._submit(request_json)
response_json = await self._submit(request_json, sign=bool(public_did))
_, parsed_schema_json = await indy.ledger.parse_get_schema_response(
response_json
)
Expand All @@ -282,6 +287,11 @@ async def send_credential_definition(self, schema_id: str, tag: str = "default")
"""

public_did = await self.wallet.get_public_did()
if not public_did:
raise BadLedgerRequestError(
"Cannot publish credential definition without a public DID"
)

schema = await self.get_schema(schema_id)

# TODO: add support for tag, sig type, and config
Expand Down Expand Up @@ -351,10 +361,10 @@ async def fetch_credential_definition(self, credential_definition_id: str):
public_did = await self.wallet.get_public_did()

request_json = await indy.ledger.build_get_cred_def_request(
public_did.did, credential_definition_id
public_did.did if public_did else None, credential_definition_id
)

response_json = await self._submit(request_json)
response_json = await self._submit(request_json, sign=bool(public_did))

(
_,
Expand Down
136 changes: 100 additions & 36 deletions aries_cloudagent/ledger/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from aries_cloudagent.ledger.indy import (
IndyLedger,
GENESIS_TRANSACTION_PATH,
BadLedgerRequestError,
ClosedPoolError,
LedgerTransactionError,
DuplicateSchemaError,
Expand Down Expand Up @@ -80,19 +81,22 @@ async def test_submit_signed(
mock_set_proto,
):

mock_did = async_mock.MagicMock()

future = asyncio.Future()
future.set_result(mock_did)

mock_sign_submit.return_value = '{"op": "REPLY"}'

mock_wallet = async_mock.MagicMock()
mock_wallet.get_public_did.return_value = future

ledger = IndyLedger("name", mock_wallet, "genesis_transactions")

async with ledger:
mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_wallet.get_public_did.return_value = None

with self.assertRaises(BadLedgerRequestError):
await ledger._submit("{}", True)

mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_did = mock_wallet.get_public_did.return_value

await ledger._submit("{}", True)

mock_wallet.get_public_did.assert_called_once_with()
Expand Down Expand Up @@ -130,7 +134,7 @@ async def test_submit_unsigned(
async with ledger:
await ledger._submit("{}", False)

mock_wallet.get_public_did.assert_called_once_with()
mock_wallet.get_public_did.assert_not_called()

mock_submit.assert_called_once_with(ledger.pool_handle, "{}")

Expand Down Expand Up @@ -177,8 +181,8 @@ async def test_submit_rejected(
await ledger._submit("{}", False)
assert "Ledger rejected transaction request" in str(context.exception)

@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aenter__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aexit__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_open")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_close")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._submit")
@async_mock.patch("indy.anoncreds.issuer_create_schema")
@async_mock.patch("indy.ledger.build_schema_request")
Expand All @@ -187,17 +191,27 @@ async def test_send_schema(
mock_build_schema_req,
mock_create_schema,
mock_submit,
mock_aexit,
mock_aenter,
mock_close,
mock_open,
):
mock_did = async_mock.MagicMock()

mock_wallet = async_mock.MagicMock()
mock_wallet.get_public_did.return_value = mock_did

ledger = IndyLedger("name", mock_wallet, "genesis_transactions")

mock_create_schema.return_value = ("schema_id", "{}")

async with ledger:
mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_wallet.get_public_did.return_value = None

with self.assertRaises(BadLedgerRequestError):
schema_id = await ledger.send_schema(
"schema_name", "schema_version", [1, 2, 3]
)

mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_did = mock_wallet.get_public_did.return_value

schema_id = await ledger.send_schema(
"schema_name", "schema_version", [1, 2, 3]
)
Expand Down Expand Up @@ -253,8 +267,8 @@ async def test_send_schema_already_exists(
== f"{mock_wallet.get_public_did.return_value.did}:{2}:schema_name:schema_version"
)

@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aenter__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aexit__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_open")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_close")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._submit")
@async_mock.patch("indy.anoncreds.issuer_create_schema")
@async_mock.patch("indy.ledger.build_get_schema_request")
Expand All @@ -265,13 +279,12 @@ async def test_get_schema(
mock_build_get_schema_req,
mock_create_schema,
mock_submit,
mock_aexit,
mock_aenter,
mock_close,
mock_open,
):
mock_did = async_mock.MagicMock()

mock_wallet = async_mock.MagicMock()
mock_wallet.get_public_did.return_value = mock_did
mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_did = mock_wallet.get_public_did.return_value

mock_parse_get_schema_req.return_value = (None, "{}")

Expand All @@ -282,39 +295,90 @@ async def test_get_schema(

mock_wallet.get_public_did.assert_called_once_with()
mock_build_get_schema_req.assert_called_once_with(mock_did.did, "schema_id")
mock_submit.assert_called_once_with(mock_build_get_schema_req.return_value)
mock_submit.assert_called_once_with(
mock_build_get_schema_req.return_value, sign=True
)
mock_parse_get_schema_req.assert_called_once_with(mock_submit.return_value)

assert response == json.loads(mock_parse_get_schema_req.return_value[1])

@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.get_schema")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aenter__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger.__aexit__")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_open")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_close")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._submit")
@async_mock.patch("indy.anoncreds.issuer_create_schema")
@async_mock.patch("indy.ledger.build_schema_request")
@async_mock.patch("indy.anoncreds.issuer_create_and_store_credential_def")
@async_mock.patch("indy.ledger.build_cred_def_request")
async def test_send_credential_definition(
self,
mock_build_schema_req,
mock_create_schema,
mock_build_cred_def,
mock_create_store_cred_def,
mock_submit,
mock_aexit,
mock_aenter,
mock_close,
mock_open,
mock_get_schema,
):
mock_did = async_mock.MagicMock()

mock_wallet = async_mock.MagicMock()
mock_wallet.get_public_did.return_value = mock_did

mock_get_schema.return_value = "{}"
cred_id = "cred_id"
cred_json = "[]"
mock_create_store_cred_def.return_value = (cred_id, cred_json)

ledger = IndyLedger("name", mock_wallet, "genesis_transactions")

schema_id = "schema_id"
tag = "tag"

async with ledger:

mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_wallet.get_public_did.return_value = None

with self.assertRaises(BadLedgerRequestError):
await ledger.send_credential_definition(schema_id, tag)

mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_did = mock_wallet.get_public_did.return_value

result_id = await ledger.send_credential_definition(schema_id, tag)
assert result_id == cred_id

mock_wallet.get_public_did.assert_called_once_with()
mock_get_schema.assert_called_once_with(schema_id)

mock_build_cred_def.assert_called_once_with(mock_did.did, cred_json)

@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_open")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._context_close")
@async_mock.patch("aries_cloudagent.ledger.indy.IndyLedger._submit")
@async_mock.patch("indy.anoncreds.issuer_create_schema")
@async_mock.patch("indy.ledger.build_get_cred_def_request")
@async_mock.patch("indy.ledger.parse_get_cred_def_response")
async def test_get_credential_definition(
self,
mock_parse_get_cred_def_req,
mock_build_get_cred_def_req,
mock_create_schema,
mock_submit,
mock_close,
mock_open,
):
mock_wallet = async_mock.MagicMock()
mock_wallet.get_public_did = async_mock.CoroutineMock()
mock_did = mock_wallet.get_public_did.return_value

mock_parse_get_cred_def_req.return_value = (None, "{}")

ledger = IndyLedger("name", mock_wallet, "genesis_transactions")

async with ledger:
credential_definition_id = await ledger.get_schema("schema_id", "tag")
response = await ledger.get_credential_definition("cred_def_id")

mock_wallet.get_public_did.assert_called_once_with()
mock_get_schema.assert_called_once_with(
mock_wallet.get_public_did.return_value
mock_build_get_cred_def_req.assert_called_once_with(mock_did.did, "cred_def_id")
mock_submit.assert_called_once_with(
mock_build_get_cred_def_req.return_value, sign=True
)
mock_parse_get_cred_def_req.assert_called_once_with(mock_submit.return_value)

assert response == json.loads(mock_parse_get_cred_def_req.return_value[1])
Loading