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

Ledger and wallet config updates; add support for transaction author agreements #127

Merged
merged 13 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ version: 2
jobs:
agent-build:
docker:
- image: bcgovimages/von-image:py36-1.9-0
- image: bcgovimages/von-image:py36-1.11-0
steps:
- checkout
- restore_cache:
keys:
- v4-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}
- v4-pip-dependencies-{{ .Branch }}-
- v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}
- v5-pip-dependencies-{{ .Branch }}-
- run:
name: Install Python Dependencies
command: |
Expand All @@ -20,7 +20,7 @@ jobs:
- save_cache:
paths:
- /home/indy/.local/lib/python3.6/site-packages
key: v4-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}
key: v5-pip-dependencies-{{ .Branch }}-{{ checksum "requirements.txt" }}-{{ checksum "requirements.dev.txt" }}

- run:
name: Run Agent Tests
Expand Down
2 changes: 1 addition & 1 deletion DevReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Most configuration parameters are provided to the the agent at startup. Refer to
It is possible to provision an Indy wallet before running an agent to avoid passing in the wallet seed on every invocation of `aca-py start`.

```bash
aca-py provision wallet --wallet-type indy --seed $SEED
aca-py provision --wallet-type indy --seed $SEED
```

For additional options, execute `aca-py provision --help`.
Expand Down
14 changes: 7 additions & 7 deletions aries_cloudagent/commands/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ def execute(argv: Sequence[str] = None):
from . import available_commands, load_command

parser = ArgumentParser()
parser.add_argument(
"-v",
"--version",
action="store_true",
help="print application version and exit",
)
subparsers = parser.add_subparsers()
for cmd in available_commands():
if cmd["name"] == "help":
parser.add_argument(
"-v",
"--version",
action="store_true",
help="print application version and exit",
)
continue
module = load_command(cmd["name"])
subparser = subparsers.add_parser(cmd["name"], help=cmd["summary"])
module.init_argument_parser(subparser)
args = parser.parse_args()
args = parser.parse_args(argv)
if args.version:
print(__version__)
else:
Expand Down
54 changes: 13 additions & 41 deletions aries_cloudagent/commands/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from typing import Sequence

from ..config import argparse as arg
from ..config.base import BaseError
from ..config.default_context import DefaultContextBuilder
from ..config.ledger import ledger_config
from ..config.util import common_config
from ..error import BaseError
from ..wallet.base import BaseWallet
from ..wallet.crypto import seed_to_did
from ..config.wallet import wallet_config


class ProvisionError(BaseError):
Expand All @@ -23,61 +23,33 @@ def init_argument_parser(parser: ArgumentParser):
)


async def provision(category: str, settings: dict):
async def provision(settings: dict):
"""Perform provisioning."""
context_builder = DefaultContextBuilder(settings)
context = await context_builder.build()

if category == "wallet":
# Initialize wallet
wallet: BaseWallet = await context.inject(BaseWallet)
if wallet.type != "indy":
raise ProvisionError("Cannot provision a non-Indy wallet type")
if wallet.created:
print("Created new wallet")
else:
print("Opened existing wallet")
print("Wallet type:", wallet.type)
print("Wallet name:", wallet.name)
wallet_seed = context.settings.get("wallet.seed")
public_did_info = await wallet.get_public_did()
if public_did_info:
# If we already have a registered public did and it doesn't match
# the one derived from `wallet_seed` then we error out.
# TODO: Add a command to change public did explicitly
if wallet_seed and seed_to_did(wallet_seed) != public_did_info.did:
raise ProvisionError(
"New seed provided which doesn't match the registered"
+ f" public did {public_did_info.did}"
)
elif wallet_seed:
public_did_info = await wallet.create_public_did(seed=wallet_seed)
print("Created new public DID")
if public_did_info:
print("Public DID:", public_did_info.did)
print("Verkey:", public_did_info.verkey)
try:
public_did = await wallet_config(context, True)

if await ledger_config(context, public_did, True):
print("Ledger configured")
else:
print("No public DID")
print("Ledger not configured")
except BaseError as e:
raise ProvisionError("Error during provisioning") from e


def execute(argv: Sequence[str] = None):
"""Entrypoint."""
parser = ArgumentParser()
parser.prog += " provision"
parser.add_argument(
dest="provision_category",
type=str,
metavar=("<category>"),
choices=["wallet"],
help="The provision command to invoke",
)
get_settings = init_argument_parser(parser)
args = parser.parse_args(argv)
settings = get_settings(args)
common_config(settings)

loop = asyncio.get_event_loop()
loop.run_until_complete(provision(args.provision_category, settings))
loop.run_until_complete(provision(settings))


if __name__ == "__main__":
Expand Down
18 changes: 14 additions & 4 deletions aries_cloudagent/commands/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio
import functools
import logging
import os
import signal
from argparse import ArgumentParser
Expand All @@ -12,6 +13,8 @@
from ..config.default_context import DefaultContextBuilder
from ..config.util import common_config

LOGGER = logging.getLogger(__name__)


async def start_app(conductor: Conductor):
"""Start up."""
Expand Down Expand Up @@ -57,6 +60,14 @@ def execute(argv: Sequence[str] = None):
def run_loop(startup: Coroutine, shutdown: Coroutine):
"""Execute the application, handling signals and ctrl-c."""

async def init(cleanup):
"""Perform startup, terminating if an exception occurs."""
try:
await startup
except Exception:
LOGGER.exception("Exception during startup:")
cleanup()

async def done():
"""Run shutdown and clean up any outstanding tasks."""
await shutdown
Expand All @@ -72,10 +83,9 @@ async def done():
asyncio.get_event_loop().stop()

loop = asyncio.get_event_loop()
loop.add_signal_handler(
signal.SIGTERM, functools.partial(asyncio.ensure_future, done(), loop=loop)
)
asyncio.ensure_future(startup, loop=loop)
cleanup = functools.partial(asyncio.ensure_future, done(), loop=loop)
loop.add_signal_handler(signal.SIGTERM, cleanup)
asyncio.ensure_future(init(cleanup), loop=loop)

try:
loop.run_forever()
Expand Down
6 changes: 5 additions & 1 deletion aries_cloudagent/commands/tests/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@

class TestHelp(AsyncTestCase):
def test_exec_help(self):
command.execute([])
with async_mock.patch.object(
command.ArgumentParser, "print_help"
) as print_help:
command.execute([])
print_help.assert_called_once()
12 changes: 4 additions & 8 deletions aries_cloudagent/commands/tests/test_provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@


class TestProvision(AsyncTestCase):
def test_bad_category(self):
with async_mock.patch.object(
command.ArgumentParser, "print_usage"
) as print_usage:
with self.assertRaises(SystemExit):
command.execute([])
print_usage.assert_called_once()
def test_bad_calls(self):
with self.assertRaises(command.ProvisionError):
command.execute([])

with self.assertRaises(SystemExit):
command.execute(["bad"])

@pytest.mark.indy
def test_provision_wallet(self):
test_seed = "testseed000000000000000000000001"
command.execute(["wallet", "--wallet-type", "indy", "--seed", test_seed])
command.execute(["--wallet-type", "indy", "--seed", test_seed])
6 changes: 3 additions & 3 deletions aries_cloudagent/commands/tests/test_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ async def test_run_loop(self):
with async_mock.patch.object(command, "asyncio", autospec=True) as mock_asyncio:
command.run_loop(startup_call, shutdown_call)
mock_asyncio.get_event_loop.return_value.add_signal_handler.assert_called_once()
mock_asyncio.ensure_future.assert_called_once_with(
startup_call, loop=mock_asyncio.get_event_loop.return_value
)
init_coro = mock_asyncio.ensure_future.call_args[0][0]
mock_asyncio.get_event_loop.return_value.run_forever.assert_called_once()
await init_coro
startup.assert_awaited_once()

done_calls = (
mock_asyncio.get_event_loop.return_value.add_signal_handler.call_args
Expand Down
45 changes: 7 additions & 38 deletions aries_cloudagent/conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from .admin.server import AdminServer
from .config.default_context import ContextBuilder
from .config.injection_context import InjectionContext
from .config.ledger import ledger_config
from .config.logging import LoggingConfigurator
from .config.wallet import wallet_config
from .dispatcher import Dispatcher
from .error import StartupError
from .ledger.base import BaseLedger
from .messaging.connections.manager import ConnectionManager, ConnectionManagerError
from .messaging.connections.models.connection_record import ConnectionRecord
from .messaging.error import MessageParseError, MessagePrepareError
Expand All @@ -34,8 +34,6 @@
from .transport.inbound.manager import InboundTransportManager
from .transport.outbound.manager import OutboundTransportManager
from .transport.outbound.queue.base import BaseOutboundMessageQueue
from .wallet.base import BaseWallet
from .wallet.crypto import seed_to_did


class Conductor:
Expand Down Expand Up @@ -143,32 +141,11 @@ async def start(self) -> None:

context = self.context

# Initialize wallet
wallet: BaseWallet = await context.inject(BaseWallet)
wallet_seed = context.settings.get("wallet.seed")
public_did_info = await wallet.get_public_did()
public_did = None
if public_did_info:
public_did = public_did_info.did
# If we already have a registered public did and it doesn't match
# the one derived from `wallet_seed` then we error out.
# TODO: Add a command to change public did explicitly
if wallet_seed and seed_to_did(wallet_seed) != public_did_info.did:
raise StartupError(
"New seed provided which doesn't match the registered"
+ f" public did {public_did_info.did}"
)
elif wallet_seed:
public_did_info = await wallet.create_public_did(seed=wallet_seed)
public_did = public_did_info.did

# Publish endpoint if necessary
endpoint = context.settings.get("default_endpoint")
if public_did:
ledger = await context.inject(BaseLedger, required=False)
if ledger:
async with ledger:
await ledger.update_endpoint_for_did(public_did, endpoint)
# Configure the wallet
public_did = await wallet_config(context)

# Configure the ledger
await ledger_config(context, public_did)

# Start up transports
try:
Expand Down Expand Up @@ -201,14 +178,6 @@ async def start(self) -> None:
self.admin_server,
)

# Debug settings
test_seed = context.settings.get("debug.seed")
if context.settings.get("debug.enabled"):
if not test_seed:
test_seed = "testseed000000000000000000000001"
if test_seed:
await wallet.create_local_did(test_seed)

# Print an invitation to the terminal
if context.settings.get("debug.print_invitation"):
try:
Expand Down
25 changes: 23 additions & 2 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def get_settings(self, args: Namespace) -> dict:
return settings


@group(CAT_START)
@group(CAT_START, CAT_PROVISION)
class LedgerGroup(ArgumentGroup):
"""Ledger settings."""

Expand All @@ -292,7 +292,10 @@ class LedgerGroup(ArgumentGroup):
def add_arguments(self, parser: ArgumentParser):
"""Add ledger-specific command line arguments to the parser."""
parser.add_argument(
"--pool-name", type=str, metavar="<pool-name>", help="Specify the pool name"
"--ledger-pool-name",
type=str,
metavar="<ledger-pool-name>",
help="Specify the pool name",
)
parser.add_argument(
"--genesis-transactions",
Expand All @@ -301,6 +304,13 @@ def add_arguments(self, parser: ArgumentParser):
metavar="<genesis-transactions>",
help="Specify the genesis transactions as a string",
)
parser.add_argument(
"--genesis-file",
type=str,
dest="genesis_file",
metavar="<genesis-file>",
help="Specify a file from which to read the genesis transactions",
)
parser.add_argument(
"--genesis-url",
type=str,
Expand All @@ -314,8 +324,12 @@ def get_settings(self, args: Namespace) -> dict:
settings = {}
if args.genesis_url:
settings["ledger.genesis_url"] = args.genesis_url
elif args.genesis_file:
settings["ledger.genesis_file"] = args.genesis_file
elif args.genesis_transactions:
settings["ledger.genesis_transactions"] = args.genesis_transactions
if args.ledger_pool_name:
settings["ledger.pool_name"] = args.ledger_pool_name
return settings


Expand Down Expand Up @@ -526,6 +540,11 @@ def add_arguments(self, parser: ArgumentParser):
+ 'e.g., \'{"account":"postgres","password":"mysecretpassword",'
+ '"admin_account":"postgres","admin_password":"mysecretpassword"}\'',
)
parser.add_argument(
"--replace-public-did",
action="store_true",
help="Allow the public DID to be replaced when a new seed is provided",
)

def get_settings(self, args: Namespace) -> dict:
"""Extract wallet settings."""
Expand All @@ -544,4 +563,6 @@ def get_settings(self, args: Namespace) -> dict:
settings["wallet.storage_config"] = args.wallet_storage_config
if args.wallet_storage_creds:
settings["wallet.storage_creds"] = args.wallet_storage_creds
if args.replace_public_did:
settings["wallet.replace_public_did"] = True
return settings
4 changes: 2 additions & 2 deletions aries_cloudagent/config/error.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Errors for config modules."""

from ..error import BaseError
from .base import ConfigError


class ArgsParseError(BaseError):
class ArgsParseError(ConfigError):
"""Error raised when there is a problem parsing the command-line arguments."""
Loading