Skip to content

Commit

Permalink
additional changes
Browse files Browse the repository at this point in the history
Signed-off-by: Shaanjot Gill <[email protected]>
  • Loading branch information
shaangill025 committed Jul 24, 2023
1 parent e412593 commit 1dc452b
Show file tree
Hide file tree
Showing 17 changed files with 282 additions and 106 deletions.
8 changes: 6 additions & 2 deletions Multiledger.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Multi-ledger in ACA-Py <!-- omit in toc -->

Ability to use multiple Indy ledgers (both IndySdk and IndyVdr) for resolving a `DID` by the ACA-Py agent. For read requests, checking of multiple ledgers in parallel is done dynamically according to logic detailed in [Read Requests Ledger Selection](#read-requests). For write requests, dynamic allocation of `write_ledger` is not supported. Write ledger can be assigned using `is_write` in the [configuration](#config-properties) or using any of the `--genesis-url`, `--genesis-file`, and `--genesis-transactions` startup (ACA-Py) arguments. If no write ledger is assigned then a `ConfigError` is raised.
Ability to use multiple Indy ledgers (both IndySdk and IndyVdr) for resolving a `DID` by the ACA-Py agent. For read requests, checking of multiple ledgers in parallel is done dynamically according to logic detailed in [Read Requests Ledger Selection](#read-requests). For write requests, dynamic allocation of `write_ledger` is supported. Configurable write ledgers can be assigned using `is_write` in the [configuration](#config-properties) or using any of the `--genesis-url`, `--genesis-file`, and `--genesis-transactions` startup (ACA-Py) arguments. If no write ledger is assigned then a `ConfigError` is raised.

More background information including problem statement, design (algorithm) and more can be found [here](https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA).

Expand Down Expand Up @@ -52,7 +52,7 @@ Optional properties:
- `pool_name`: name of the indy pool to be opened
- `keepalive`: how many seconds to keep the ledger open
- `socks_proxy`
- `is_write`: Whether the ledger is the write ledger. Only one ledger can be assigned, otherwise a `ConfigError` is raised.
- `is_write`: Whether this ledger is writable/can be write configurable. Multiple write ledgers can be specified in config.


## Multi-ledger Admin API
Expand All @@ -63,6 +63,10 @@ Multi-ledger related actions are grouped under the `ledger` topic in the Swagger
Returns the multiple ledger configuration currently in use
- `/ledger/multiple/get-write-ledger`:
Returns the current active/set `write_ledger's` `ledger_id`
- `/ledger/multiple/get-write-ledgers`:
Returns list of available `write_ledger's` `ledger_id`
- `/ledger/multiple/{ledger_id}/set-write-ledger`:
Set active `write_ledger's` `ledger_id`

## Ledger Selection

Expand Down
45 changes: 43 additions & 2 deletions aries_cloudagent/askar/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,49 @@ def bind_providers(self):
ref(self),
),
)

if self.ledger_pool:
if (
self.settings.get("ledger.ledger_config_list")
and len(self.settings.get("ledger.ledger_config_list")) >= 1
):
write_ledger_config = None
prod_write_ledger_pool = []
non_prod_write_ledger_pool = []
for ledger_config in self.settings.get("ledger.ledger_config_list"):
if ledger_config.get("is_production") and ledger_config.get("is_write"):
prod_write_ledger_pool.append(ledger_config)
elif not ledger_config.get("is_production") and ledger_config.get(
"is_write"
):
non_prod_write_ledger_pool.append(ledger_config)
cache = self.context.injector.inject_or(BaseCache)
if len(prod_write_ledger_pool) >= 1:
write_ledger_config = prod_write_ledger_pool[0]
elif len(non_prod_write_ledger_pool) >= 1:
write_ledger_config = non_prod_write_ledger_pool[0]
else:
raise ProfileError(
"No write ledger configuration found in ledger_config_list which "
"was provided with --genesis-transactions-list"
)
injector.bind_provider(
BaseLedger,
ClassProvider(
IndyVdrLedger,
IndyVdrLedgerPool(
write_ledger_config.get("pool_name")
or write_ledger_config.get("id"),
keepalive=write_ledger_config.get("keepalive"),
cache=cache,
genesis_transactions=write_ledger_config.get(
"genesis_transactions"
),
read_only=write_ledger_config.get("read_only"),
socks_proxy=write_ledger_config.get("socks_proxy"),
),
ref(self),
),
)
elif self.ledger_pool:
injector.bind_provider(
BaseLedger, ClassProvider(IndyVdrLedger, self.ledger_pool, ref(self))
)
Expand Down
38 changes: 18 additions & 20 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ def get_settings(self, args: Namespace) -> dict:
single_configured = False
multi_configured = False
update_pool_name = False
write_ledger_specified = False
if args.genesis_url:
settings["ledger.genesis_url"] = args.genesis_url
single_configured = True
Expand All @@ -930,27 +931,24 @@ def get_settings(self, args: Namespace) -> dict:
txn_config_list = yaml.safe_load(stream)
ledger_config_list = []
for txn_config in txn_config_list:
ledger_config_list.append(txn_config)
if "is_write" in txn_config and txn_config["is_write"]:
if "genesis_url" in txn_config:
settings["ledger.genesis_url"] = txn_config[
"genesis_url"
]
elif "genesis_file" in txn_config:
settings["ledger.genesis_file"] = txn_config[
"genesis_file"
]
elif "genesis_transactions" in txn_config:
settings["ledger.genesis_transactions"] = txn_config[
"genesis_transactions"
]
else:
raise ArgsParseError(
"No genesis information provided for write ledger"
)
if "id" in txn_config:
settings["ledger.pool_name"] = txn_config["id"]
update_pool_name = True
write_ledger_specified = True
if (
"genesis_url" not in txn_config
and "genesis_file" not in txn_config
and "genesis_transactions" not in txn_config
):
raise ArgsParseError(
"No genesis information provided for write ledger"
)
if "id" in txn_config and "pool_name" not in txn_config:
txn_config["pool_name"] = txn_config["id"]
update_pool_name = True
ledger_config_list.append(txn_config)
if not write_ledger_specified:
raise ArgsParseError(
"No write ledger genesis provided in multi-ledger config"
)
settings["ledger.ledger_config_list"] = ledger_config_list
multi_configured = True
if not (single_configured or multi_configured):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- id: sovrinMain
is_production: true
is_write: true
- id: sovrinStaging
is_production: true
- id: sovrinTest
is_production: false
32 changes: 32 additions & 0 deletions aries_cloudagent/config/tests/test-ledger-args-no-write.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
- id: sovrinMain
is_production: true
genesis_transactions:
reqSignature: {}
txn:
data:
data:
alias: Node1
blskey: >-
4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba
blskey_pop: >-
RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1
client_ip: 192.168.65.3
client_port: 9702
node_ip: 192.168.65.3
node_port: 9701
services:
- VALIDATOR
dest: Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv
metadata:
from: Th7MpTaRZVRYnPiabds81Y
type: '0'
txnMetadata:
seqNo: 1
txnId: fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62
ver: '1'
- id: sovrinStaging
is_production: true
genesis_file: /home/indy/ledger/sandbox/pool_transactions_genesis
- id: sovrinTest
is_production: false
genesis_url: 'http://localhost:9000/genesis'
2 changes: 2 additions & 0 deletions aries_cloudagent/config/tests/test-ledger-args.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
- id: sovrinMain
is_production: true
is_write: true
genesis_transactions:
reqSignature: {}
txn:
Expand All @@ -26,6 +27,7 @@
ver: '1'
- id: sovrinStaging
is_production: true
is_write: true
genesis_file: /home/indy/ledger/sandbox/pool_transactions_genesis
- id: sovrinTest
is_production: false
Expand Down
31 changes: 29 additions & 2 deletions aries_cloudagent/config/tests/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,57 @@ async def test_get_genesis_transactions_list_with_ledger_selection(self):
result = parser.parse_args(
[
"--genesis-transactions-list",
"./aries_cloudagent/config/tests/test-ledger-args.yaml",
"./aries_cloudagent/config/tests/test-ledger-args-no-write.yaml",
]
)
assert (
result.genesis_transactions_list
== "./aries_cloudagent/config/tests/test-ledger-args-no-write.yaml"
)
with self.assertRaises(argparse.ArgsParseError):
settings = group.get_settings(result)

result = parser.parse_args(
[
"--genesis-transactions-list",
"./aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml",
]
)
assert (
result.genesis_transactions_list
== "./aries_cloudagent/config/tests/test-ledger-args.yaml"
== "./aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml"
)
with self.assertRaises(argparse.ArgsParseError):
settings = group.get_settings(result)

result = parser.parse_args(
[
"--genesis-transactions-list",
"./aries_cloudagent/config/tests/test-ledger-args.yaml",
]
)
assert (
result.genesis_transactions_list
== "./aries_cloudagent/config/tests/test-ledger-args.yaml"
)
settings = group.get_settings(result)

assert len(settings.get("ledger.ledger_config_list")) == 3
assert (
{
"id": "sovrinStaging",
"is_production": True,
"is_write": True,
"genesis_file": "/home/indy/ledger/sandbox/pool_transactions_genesis",
"pool_name": "sovrinStaging",
}
) in settings.get("ledger.ledger_config_list")
assert (
{
"id": "sovrinTest",
"is_production": False,
"genesis_url": "http://localhost:9000/genesis",
"pool_name": "sovrinTest",
}
) in settings.get("ledger.ledger_config_list")

Expand Down
11 changes: 3 additions & 8 deletions aries_cloudagent/config/tests/test_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ async def test_load_multiple_genesis_transactions_config_error_a(self):
)
assert "No is_write ledger set" in str(cm.exception)

async def test_load_multiple_genesis_transactions_config_error_b(self):
async def test_load_multiple_genesis_transactions_multiple_write(self):
TEST_GENESIS_TXNS = {
"reqSignature": {},
"txn": {
Expand Down Expand Up @@ -561,8 +561,7 @@ async def test_load_multiple_genesis_transactions_config_error_b(self):
"is_production": True,
"genesis_url": "http://localhost:9001/genesis",
},
],
"ledger.genesis_url": "http://localhost:9000/genesis",
]
}
with async_mock.patch.object(
test_module,
Expand All @@ -578,11 +577,7 @@ async def test_load_multiple_genesis_transactions_config_error_b(self):
)
)
)
with self.assertRaises(test_module.ConfigError) as cm:
await test_module.load_multiple_genesis_transactions_from_config(
settings
)
assert "Only a single ledger can be" in str(cm.exception)
await test_module.load_multiple_genesis_transactions_from_config(settings)

async def test_load_multiple_genesis_transactions_from_config_io_x(self):
TEST_GENESIS_TXNS = {
Expand Down
46 changes: 44 additions & 2 deletions aries_cloudagent/indy/sdk/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import warnings
from weakref import finalize, ref

from ...cache.base import BaseCache
from ...config.injection_context import InjectionContext
from ...config.provider import ClassProvider
from ...core.error import ProfileError
Expand Down Expand Up @@ -87,8 +88,49 @@ def bind_providers(self):
"aries_cloudagent.storage.vc_holder.indy.IndySdkVCHolder", self.opened
),
)

if self.ledger_pool:
if (
self.settings.get("ledger.ledger_config_list")
and len(self.settings.get("ledger.ledger_config_list")) >= 1
):
write_ledger_config = None
prod_write_ledger_pool = []
non_prod_write_ledger_pool = []
for ledger_config in self.settings.get("ledger.ledger_config_list"):
if ledger_config.get("is_production") and ledger_config.get("is_write"):
prod_write_ledger_pool.append(ledger_config)
elif not ledger_config.get("is_production") and ledger_config.get(
"is_write"
):
non_prod_write_ledger_pool.append(ledger_config)
cache = self.context.injector.inject_or(BaseCache)
if len(prod_write_ledger_pool) >= 1:
write_ledger_config = prod_write_ledger_pool[0]
elif len(non_prod_write_ledger_pool) >= 1:
write_ledger_config = non_prod_write_ledger_pool[0]
else:
raise ProfileError(
"No write ledger configuration found in ledger_config_list which "
"was provided with --genesis-transactions-list"
)
injector.bind_provider(
BaseLedger,
ClassProvider(
IndySdkLedger,
IndySdkLedgerPool(
write_ledger_config.get("pool_name")
or write_ledger_config.get("id"),
keepalive=write_ledger_config.get("keepalive"),
cache=cache,
genesis_transactions=write_ledger_config.get(
"genesis_transactions"
),
read_only=write_ledger_config.get("read_only"),
socks_proxy=write_ledger_config.get("socks_proxy"),
),
ref(self),
),
)
elif self.ledger_pool:
injector.bind_provider(
BaseLedger, ClassProvider(IndySdkLedger, self.ledger_pool, ref(self))
)
Expand Down
9 changes: 8 additions & 1 deletion aries_cloudagent/indy/sdk/tests/test_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ def test_settings_genesis_transactions(open_wallet):


def test_settings_ledger_config(open_wallet):
context = InjectionContext(settings={"ledger.ledger_config_list": True})
context = InjectionContext(
settings={
"ledger.ledger_config_list": [
async_mock.MagicMock(),
async_mock.MagicMock(),
]
}
)
context.injector.bind_instance(IndySdkLedgerPool, IndySdkLedgerPool("name"))
profile = IndySdkProfile(open_wallet, context)

Expand Down
22 changes: 7 additions & 15 deletions aries_cloudagent/ledger/multiple_ledger/indy_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,8 @@ async def get_nonprod_ledgers(self) -> Mapping:

async def get_ledger_id_by_ledger_pool_name(self, pool_name: str) -> str:
"""Return ledger_id by ledger pool name."""
for ledger_id, indy_vdr_ledger in self.production_ledgers.items():
if indy_vdr_ledger.pool_name == pool_name:
return ledger_id
for ledger_id, indy_vdr_ledger in self.non_production_ledgers.items():
multi_ledgers = self.production_ledgers | self.non_production_ledgers
for ledger_id, indy_vdr_ledger in multi_ledgers.items():
if indy_vdr_ledger.pool_name == pool_name:
return ledger_id
raise MultipleLedgerManagerError(
Expand All @@ -87,21 +85,15 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st
"""Set the write ledger for the profile."""
if ledger_id not in self.writable_ledgers:
raise MultipleLedgerManagerError(
f"Provided Ledger identifier {ledger_id} is not " "write configurable."
f"Provided Ledger identifier {ledger_id} is not write configurable."
)
if ledger_id in self.production_ledgers:
multi_ledgers = self.production_ledgers | self.non_production_ledgers
if ledger_id in multi_ledgers:
profile.context.injector.bind_instance(
BaseLedger, self.production_ledgers.get(ledger_id)
BaseLedger, multi_ledgers.get(ledger_id)
)
return ledger_id
if ledger_id in self.non_production_ledgers:
profile.context.injector.bind_instance(
BaseLedger, self.non_production_ledgers.get(ledger_id)
)
return ledger_id
raise MultipleLedgerManagerError(
f"No ledger info found for {ledger_id} is not."
)
raise MultipleLedgerManagerError(f"No ledger info found for {ledger_id}.")

async def _get_ledger_by_did(
self,
Expand Down
Loading

0 comments on commit 1dc452b

Please sign in to comment.