Skip to content

Commit

Permalink
Merge pull request #364 from ianco/read-only-ledger
Browse files Browse the repository at this point in the history
Read only ledger flag
  • Loading branch information
swcurran authored Feb 15, 2020
2 parents 0b3cdc0 + a9f7d2e commit ab8097d
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 30 deletions.
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

0 comments on commit ab8097d

Please sign in to comment.