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

Read only ledger flag #364

Merged
merged 11 commits into from
Feb 15, 2020
4 changes: 4 additions & 0 deletions aries_cloudagent/commands/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ def execute(argv: Sequence[str] = None):
settings = get_settings(args)
common_config(settings)

# provision needs write access to the ledger
# (override if specified otherwise)
settings["ledger.read_only"] = False

loop = asyncio.get_event_loop()
loop.run_until_complete(provision(settings))

Expand Down
3 changes: 3 additions & 0 deletions aries_cloudagent/commands/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def execute(argv: Sequence[str] = None):
settings = get_settings(args)
common_config(settings)

# set ledger to read only if explicitely specified
settings["ledger.read_only"] = settings.get("read_only_ledger", False)

# Support WEBHOOK_URL environment variable
webhook_url = os.environ.get("WEBHOOK_URL")
if webhook_url:
Expand Down
47 changes: 27 additions & 20 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,28 @@ def add_arguments(self, parser: ArgumentParser):
storage engine. This storage interface is used to store internal state.\
Supported internal storage types are 'basic' (memory) and 'indy'.",
)
parser.add_argument(
"-e",
"--endpoint",
type=str,
nargs="+",
metavar="<endpoint>",
help="Specifies the endpoints to put into DIDDocs\
to inform other agents of where they should send messages destined\
for this agent. Each endpoint could be one of the specified inbound\
transports for this agent, or the endpoint could be that of\
another agent (e.g. 'https://example.com/agent-endpoint') if the\
routing of messages to this agent by a mediator is configured.\
The first endpoint specified will be used in invitations.\
The endpoints are used in the formation of a connection\
with another agent.",
)
parser.add_argument(
"--read-only-ledger",
action="store_true",
help="Sets ledger to read-only to prevent updates.\
Default: false.",
)

def get_settings(self, args: Namespace) -> dict:
"""Extract general settings."""
Expand All @@ -385,6 +407,11 @@ def get_settings(self, args: Namespace) -> dict:
settings["external_plugins"] = args.external_plugins
if args.storage_type:
settings["storage.type"] = args.storage_type
if args.endpoint:
settings["default_endpoint"] = args.endpoint[0]
settings["additional_endpoints"] = args.endpoint[1:]
if args.read_only_ledger:
settings["read_only_ledger"] = True
return settings


Expand Down Expand Up @@ -590,22 +617,6 @@ def add_arguments(self, parser: ArgumentParser):
multiple times to supoort multiple transport types. Supported outbound\
transport types are 'http' and 'ws'.",
)
parser.add_argument(
"-e",
"--endpoint",
type=str,
nargs="+",
metavar="<endpoint>",
help="Specifies the endpoints to put into DIDDocs\
to inform other agents of where they should send messages destined\
for this agent. Each endpoint could be one of the specified inbound\
transports for this agent, or the endpoint could be that of\
another agent (e.g. 'https://example.com/agent-endpoint') if the\
routing of messages to this agent by a mediator is configured.\
The first endpoint specified will be used in invitations.\
The endpoints are used in the formation of a connection\
with another agent.",
)
parser.add_argument(
"-l",
"--label",
Expand All @@ -621,7 +632,6 @@ def add_arguments(self, parser: ArgumentParser):
metavar="<message-size>",
help="Set the maximum size in bytes for inbound agent messages.",
)

parser.add_argument(
"--enable-undelivered-queue",
action="store_true",
Expand All @@ -637,9 +647,6 @@ def get_settings(self, args: Namespace):
settings["transport.outbound_configs"] = args.outbound_transports
settings["transport.enable_undelivered_queue"] = args.enable_undelivered_queue

if args.endpoint:
settings["default_endpoint"] = args.endpoint[0]
settings["additional_endpoints"] = args.endpoint[1:]
if args.label:
settings["default_label"] = args.label
if args.max_message_size:
Expand Down
9 changes: 0 additions & 9 deletions aries_cloudagent/config/tests/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,16 @@ async def test_transport_settings(self):
"80",
"--outbound-transport",
"http",
"-e",
"http://default.endpoint/",
"ws://alternate.endpoint/",
]
)

assert result.inbound_transports == [["http", "0.0.0.0", "80"]]
assert result.outbound_transports == ["http"]
assert result.endpoint == [
"http://default.endpoint/",
"ws://alternate.endpoint/",
]

settings = group.get_settings(result)

assert settings.get("transport.inbound_configs") == [["http", "0.0.0.0", "80"]]
assert settings.get("transport.outbound_configs") == ["http"]
assert settings.get("default_endpoint") == "http://default.endpoint/"
assert settings.get("additional_endpoints") == ["ws://alternate.endpoint/"]

def test_bytesize(self):
bs = ByteSize()
Expand Down
22 changes: 22 additions & 0 deletions aries_cloudagent/ledger/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(
keepalive: int = 0,
cache: BaseCache = None,
cache_duration: int = 600,
read_only: bool = False,
):
"""
Initialize an IndyLedger instance.
Expand All @@ -113,6 +114,7 @@ def __init__(
self.pool_name = pool_name
self.taa_acceptance = None
self.taa_cache = None
self.read_only = read_only

if wallet.WALLET_TYPE != "indy":
raise LedgerConfigError("Wallet type is not 'indy'")
Expand Down Expand Up @@ -314,6 +316,11 @@ async def send_schema(
if schema_id:
self.logger.warning("Schema already exists on ledger. Returning ID.")
else:
if self.read_only:
raise LedgerError(
"Error cannot write schema when ledger is in read only mode"
)

with IndyErrorHandler("Exception when creating schema definition"):
schema_id, schema_json = await indy.anoncreds.issuer_create_schema(
public_info.did,
Expand Down Expand Up @@ -556,6 +563,11 @@ async def cred_def_in_wallet(wallet_handle, cred_def_id) -> bool:
except IndyError as error:
raise IndyErrorHandler.wrap_error(error) from error
else: # created cred def in wallet OK
if self.read_only:
raise LedgerError(
"Error cannot write cred def when ledger is in read only mode"
)

wallet_cred_def = json.loads(credential_definition_json)
with IndyErrorHandler("Exception when building cred def request"):
request_json = await indy.ledger.build_cred_def_request(
Expand Down Expand Up @@ -714,6 +726,11 @@ async def update_endpoint_for_did(self, did: str, endpoint: str) -> bool:
"""
exist_endpoint = await self.get_endpoint_for_did(did)
if exist_endpoint != endpoint:
if self.read_only:
raise LedgerError(
"Error cannot update endpoint when ledger is in read only mode"
)

nym = self.did_to_nym(did)
attr_json = json.dumps({"endpoint": {"endpoint": endpoint}})
with IndyErrorHandler("Exception when building attribute request"):
Expand All @@ -736,6 +753,11 @@ async def register_nym(
alias: Human-friendly alias to assign to the DID.
role: For permissioned ledgers, what role should the new DID have.
"""
if self.read_only:
raise LedgerError(
"Error cannot register nym when ledger is in read only mode"
)

public_info = await self.wallet.get_public_did()
public_did = public_info.did if public_info else None
r = await indy.ledger.build_nym_request(public_did, did, verkey, alias, role)
Expand Down
6 changes: 5 additions & 1 deletion aries_cloudagent/ledger/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ async def provide(self, settings: BaseSettings, injector: BaseInjector):

pool_name = settings.get("ledger.pool_name", "default")
keepalive = int(settings.get("ledger.keepalive", 5))
read_only = bool(settings.get("ledger.read_only", False))
if read_only:
LOGGER.error("Note: setting ledger to read-only mode")
wallet = await injector.inject(BaseWallet)
ledger = None

if wallet.WALLET_TYPE == "indy":
IndyLedger = ClassLoader.load_class(self.LEDGER_CLASSES["indy"])
cache = await injector.inject(BaseCache, required=False)
ledger = IndyLedger(pool_name, wallet, keepalive=keepalive, cache=cache)
ledger = IndyLedger(pool_name, wallet, keepalive=keepalive, cache=cache,
read_only=read_only)

genesis_transactions = settings.get("ledger.genesis_transactions")
if genesis_transactions:
Expand Down