From d83bae376565fd8e4081b1eb8633e2c5ed5c2976 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 21 Jul 2023 08:55:30 -0700 Subject: [PATCH 1/8] initial impl for selectable write ledger Signed-off-by: Shaanjot Gill --- aries_cloudagent/config/ledger.py | 4 +- aries_cloudagent/core/conductor.py | 8 +- .../ledger/multiple_ledger/base_manager.py | 12 ++- .../ledger/multiple_ledger/indy_manager.py | 57 +++++++++---- .../multiple_ledger/indy_vdr_manager.py | 43 ++++++++-- .../multiple_ledger/ledger_config_schema.py | 13 ++- .../multiple_ledger/manager_provider.py | 30 +++---- aries_cloudagent/ledger/routes.py | 85 ++++++++++++++++++- aries_cloudagent/revocation/routes.py | 8 +- aries_cloudagent/tails/indy_tails_server.py | 9 +- 10 files changed, 206 insertions(+), 63 deletions(-) diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index 8ff0f66640..03574d6521 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -89,9 +89,7 @@ async def load_multiple_genesis_transactions_from_config(settings: Settings): False if config.get("is_write") is None else config.get("is_write") ) ledger_id = config.get("id") or str(uuid.uuid4()) - if is_write_ledger and write_ledger_set: - raise ConfigError("Only a single ledger can be is_write") - elif is_write_ledger: + if is_write_ledger: write_ledger_set = True ledger_txns_list.append( { diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index 0a53a420d4..5a922744ee 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -34,7 +34,7 @@ ) from ..core.profile import Profile from ..indy.verifier import IndyVerifier - +from ..ledger.base import BaseLedger from ..ledger.error import LedgerConfigError, LedgerTransactionError from ..ledger.multiple_ledger.base_manager import ( BaseMultipleLedgerManager, @@ -144,11 +144,7 @@ async def setup(self): MultiIndyLedgerManagerProvider(self.root_profile), ) if not (context.settings.get("ledger.genesis_transactions")): - ledger = ( - await context.injector.inject( - BaseMultipleLedgerManager - ).get_write_ledger() - )[1] + ledger = context.injector.inject(BaseLedger) if ( self.root_profile.BACKEND_NAME == "askar" and ledger.BACKEND_NAME == "indy-vdr" diff --git a/aries_cloudagent/ledger/multiple_ledger/base_manager.py b/aries_cloudagent/ledger/multiple_ledger/base_manager.py index 346f36af77..78506c0d7a 100644 --- a/aries_cloudagent/ledger/multiple_ledger/base_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/base_manager.py @@ -1,7 +1,7 @@ """Manager for multiple ledger.""" from abc import ABC, abstractmethod -from typing import Optional, Tuple, Mapping +from typing import Optional, Tuple, Mapping, List from ...core.error import BaseError from ...core.profile import Profile @@ -20,9 +20,17 @@ def __init__(self, profile: Profile): """Initialize Multiple Ledger Manager.""" @abstractmethod - async def get_write_ledger(self) -> Tuple[str, BaseLedger]: + async def get_write_ledgers(self) -> List[str]: """Return write ledger.""" + @abstractmethod + async def get_ledger_id_by_ledger_pool_name(self, pool_name: str) -> str: + """Return ledger_id by ledger pool name.""" + + @abstractmethod + async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> str: + """Set the write ledger for the profile.""" + @abstractmethod async def get_prod_ledgers(self) -> Mapping: """Return configured production ledgers.""" diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 9ba534b19f..2e7a1a7c0d 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -5,7 +5,7 @@ import json from collections import OrderedDict -from typing import Optional, Tuple, Mapping +from typing import Optional, Tuple, Mapping, List from ...cache.base import BaseCache from ...core.profile import Profile @@ -33,7 +33,7 @@ def __init__( profile: Profile, production_ledgers: OrderedDict = OrderedDict(), non_production_ledgers: OrderedDict = OrderedDict(), - write_ledger_info: Tuple[str, IndySdkLedger] = None, + writable_ledgers: set = set(), cache_ttl: int = None, ): """Initialize MultiIndyLedgerManager. @@ -48,17 +48,13 @@ def __init__( self.profile = profile self.production_ledgers = production_ledgers self.non_production_ledgers = non_production_ledgers - self.write_ledger_info = write_ledger_info + self.writable_ledgers = writable_ledgers self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) self.cache_ttl = cache_ttl - async def get_write_ledger(self) -> Optional[Tuple[str, IndySdkLedger]]: + async def get_write_ledgers(self) -> List[str]: """Return the write IndySdkLedger instance.""" - # return self.write_ledger_info - if self.write_ledger_info: - return (self.write_ledger_info[0], self.profile.inject_or(BaseLedger)) - else: - return None + return list(self.writable_ledgers) async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: """Return BaseLedger instance.""" @@ -74,6 +70,39 @@ async def get_nonprod_ledgers(self) -> Mapping: """Return non_production ledgers mapping.""" return self.non_production_ledgers + 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(): + if indy_vdr_ledger.pool_name == pool_name: + return ledger_id + raise MultipleLedgerManagerError( + f"Provided Ledger pool name {pool_name} not found " + "in either production_ledgers or non_production_ledgers" + ) + + async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> str: + """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." + ) + if ledger_id in self.production_ledgers: + profile.context.injector.bind_instance( + BaseLedger, self.production_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." + ) + async def _get_ledger_by_did( self, ledger_id: str, @@ -94,11 +123,7 @@ async def _get_ledger_by_did( """ try: indy_sdk_ledger = None - if self.write_ledger_info and ledger_id == self.write_ledger_info[0]: - indy_sdk_ledger = await self.get_write_ledger() - if indy_sdk_ledger: - indy_sdk_ledger = indy_sdk_ledger[1] - elif ledger_id in self.production_ledgers: + if ledger_id in self.production_ledgers: indy_sdk_ledger = self.production_ledgers.get(ledger_id) else: indy_sdk_ledger = self.non_production_ledgers.get(ledger_id) @@ -149,9 +174,7 @@ async def lookup_did_in_configured_ledgers( cache_key = f"did_ledger_id_resolver::{did}" if bool(cache_did and self.cache and await self.cache.get(cache_key)): cached_ledger_id = await self.cache.get(cache_key) - if self.write_ledger_info and cached_ledger_id == self.write_ledger_info[0]: - return self.get_write_ledger() - elif cached_ledger_id in self.production_ledgers: + if cached_ledger_id in self.production_ledgers: return (cached_ledger_id, self.production_ledgers.get(cached_ledger_id)) elif cached_ledger_id in self.non_production_ledgers: return ( diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py index 0c4d09aa26..f96f6e90c4 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py @@ -5,7 +5,7 @@ import json from collections import OrderedDict -from typing import Optional, Tuple, Mapping +from typing import Optional, Tuple, Mapping, List from ...cache.base import BaseCache from ...core.profile import Profile @@ -33,7 +33,7 @@ def __init__( profile: Profile, production_ledgers: OrderedDict = OrderedDict(), non_production_ledgers: OrderedDict = OrderedDict(), - write_ledger_info: Tuple[str, IndyVdrLedger] = None, + writable_ledgers: set = set(), cache_ttl: int = None, ): """Initialize MultiIndyLedgerManager. @@ -48,13 +48,13 @@ def __init__( self.profile = profile self.production_ledgers = production_ledgers self.non_production_ledgers = non_production_ledgers - self.write_ledger_info = write_ledger_info + self.writable_ledgers = writable_ledgers self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) self.cache_ttl = cache_ttl - async def get_write_ledger(self) -> Optional[Tuple[str, IndyVdrLedger]]: + async def get_write_ledgers(self) -> List[str]: """Return the write IndyVdrLedger instance.""" - return self.write_ledger_info + return list(self.writable_ledgers) async def get_prod_ledgers(self) -> Mapping: """Return production ledgers mapping.""" @@ -64,12 +64,45 @@ async def get_nonprod_ledgers(self) -> Mapping: """Return non_production ledgers mapping.""" return self.non_production_ledgers + async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> str: + """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." + ) + if ledger_id in self.production_ledgers: + profile.context.injector.bind_instance( + BaseLedger, self.production_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." + ) + async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: """Return BaseLedger instance.""" return self.production_ledgers.get( ledger_id ) or self.non_production_ledgers.get(ledger_id) + 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(): + if indy_vdr_ledger.pool_name == pool_name: + return ledger_id + raise MultipleLedgerManagerError( + f"Provided Ledger pool name {pool_name} not found " + "in either production_ledgers or non_production_ledgers" + ) + async def _get_ledger_by_did( self, ledger_id: str, diff --git a/aries_cloudagent/ledger/multiple_ledger/ledger_config_schema.py b/aries_cloudagent/ledger/multiple_ledger/ledger_config_schema.py index d4edb9d120..692b7eaa97 100644 --- a/aries_cloudagent/ledger/multiple_ledger/ledger_config_schema.py +++ b/aries_cloudagent/ledger/multiple_ledger/ledger_config_schema.py @@ -76,11 +76,20 @@ class LedgerConfigListSchema(OpenAPISchema): ) -class WriteLedgerRequestSchema(OpenAPISchema): - """Schema for setting/getting ledger_id for the write ledger.""" +class WriteLedgerSchema(OpenAPISchema): + """Schema for getting ledger_id for the write ledger.""" ledger_id = fields.Str() +class ConfigurableWriteLedgersSchema(OpenAPISchema): + """Schema for list of configurable write ledger.""" + + write_ledgers = fields.List( + fields.Str(description="Ledgers identifiers"), + description="List of configurable write ledgers identifiers", + ) + + class MultipleLedgerModuleResultSchema(OpenAPISchema): """Schema for the multiple ledger modules endpoint.""" diff --git a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py index 7a40c2530f..08181cc759 100644 --- a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py @@ -73,7 +73,7 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): indy_sdk_production_ledgers = OrderedDict() indy_sdk_non_production_ledgers = OrderedDict() ledger_config_list = settings.get_value("ledger.ledger_config_list") - write_ledger_info = None + write_ledgers = set() for config in ledger_config_list: keepalive = config.get("keepalive") read_only = config.get("read_only") @@ -85,7 +85,7 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): ledger_is_production = config.get("is_production") ledger_is_write = config.get("is_write") if ledger_is_write: - write_ledger_info = (ledger_id, None) + write_ledgers.add(ledger_id) else: ledger_pool = pool_class( pool_name, @@ -107,24 +107,21 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): ] = ledger_instance if settings.get_value("ledger.genesis_transactions"): ledger_instance = self.root_profile.inject_or(BaseLedger) - ledger_id = "startup::" + ledger_instance.pool.name + ledger_id = ledger_instance.pool.name indy_sdk_production_ledgers[ledger_id] = ledger_instance - if not write_ledger_info: - write_ledger_info = (ledger_id, ledger_instance) - indy_sdk_production_ledgers.move_to_end( - ledger_id, last=False - ) + write_ledgers.add(ledger_id) + indy_sdk_production_ledgers.move_to_end(ledger_id, last=False) self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_sdk_production_ledgers, non_production_ledgers=indy_sdk_non_production_ledgers, - write_ledger_info=write_ledger_info, + writable_ledgers=write_ledgers, ) else: indy_vdr_production_ledgers = OrderedDict() indy_vdr_non_production_ledgers = OrderedDict() ledger_config_list = settings.get_value("ledger.ledger_config_list") - write_ledger_info = None + write_ledgers = set() for config in ledger_config_list: keepalive = config.get("keepalive") read_only = config.get("read_only") @@ -148,25 +145,22 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): profile=self.root_profile, ) if ledger_is_write: - write_ledger_info = (ledger_id, ledger_instance) + write_ledgers.add(ledger_id) if ledger_is_production: indy_vdr_production_ledgers[ledger_id] = ledger_instance else: indy_vdr_non_production_ledgers[ledger_id] = ledger_instance if settings.get_value("ledger.genesis_transactions"): ledger_instance = self.root_profile.inject_or(BaseLedger) - ledger_id = "startup::" + ledger_instance.pool.name + ledger_id = ledger_instance.pool.name indy_vdr_production_ledgers[ledger_id] = ledger_instance - if not write_ledger_info: - write_ledger_info = (ledger_id, ledger_instance) - indy_vdr_production_ledgers.move_to_end( - ledger_id, last=False - ) + write_ledgers.add(ledger_id) + indy_vdr_production_ledgers.move_to_end(ledger_id, last=False) self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_vdr_production_ledgers, non_production_ledgers=indy_vdr_non_production_ledgers, - write_ledger_info=write_ledger_info, + writable_ledgers=write_ledgers, ) except ClassNotFoundError as err: raise InjectionError( diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index 0c8505d49c..a2e4544d0a 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -4,7 +4,13 @@ import logging from aiohttp import web -from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema +from aiohttp_apispec import ( + docs, + querystring_schema, + match_info_schema, + request_schema, + response_schema, +) from marshmallow import fields, validate from ..admin.request_context import AdminRequestContext @@ -39,6 +45,7 @@ from .base import BaseLedger, Role as LedgerRole from .multiple_ledger.base_manager import ( BaseMultipleLedgerManager, + MultipleLedgerManagerError, ) from .multiple_ledger.ledger_requests_executor import ( GET_NYM_ROLE, @@ -48,7 +55,8 @@ ) from .multiple_ledger.ledger_config_schema import ( LedgerConfigListSchema, - WriteLedgerRequestSchema, + WriteLedgerSchema, + ConfigurableWriteLedgersSchema, ) from .endpoint_type import EndpointType from .error import BadLedgerRequestError, LedgerError, LedgerTransactionError @@ -214,6 +222,12 @@ class GetDIDEndpointResponseSchema(OpenAPISchema): ) +class WriteLedgerRequestSchema(OpenAPISchema): + """Schema for setting ledger_id for the write ledger.""" + + ledger_id = fields.Str(required=True) + + @docs( tags=["ledger"], summary="Send a NYM registration to the ledger.", @@ -652,8 +666,31 @@ async def ledger_accept_taa(request: web.BaseRequest): return web.json_response({}) +@docs(tags=["ledger"], summary="Fetch list of available write ledgers") +@response_schema(ConfigurableWriteLedgersSchema, 200, description="") +async def get_write_ledgers(request: web.BaseRequest): + """ + Request handler for fetching the list of available write ledgers. + + Args: + request: aiohttp request object + + Returns: + The list of write ledgers + + """ + context: AdminRequestContext = request["context"] + async with context.profile.session() as session: + multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) + if not multiledger_mgr: + reason = "Multiple ledger support not enabled" + raise web.HTTPForbidden(reason=reason) + available_write_ledgers = await multiledger_mgr.get_write_ledgers() + return web.json_response(available_write_ledgers) + + @docs(tags=["ledger"], summary="Fetch the current write ledger") -@response_schema(WriteLedgerRequestSchema, 200, description="") +@response_schema(WriteLedgerSchema, 200, description="") async def get_write_ledger(request: web.BaseRequest): """ Request handler for fetching the currently set write ledger. @@ -668,13 +705,47 @@ async def get_write_ledger(request: web.BaseRequest): context: AdminRequestContext = request["context"] async with context.profile.session() as session: multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) + write_ledger = session.inject(BaseLedger) if not multiledger_mgr: reason = "Multiple ledger support not enabled" raise web.HTTPForbidden(reason=reason) - ledger_id = (await multiledger_mgr.get_write_ledger())[0] + ledger_id = await multiledger_mgr.get_ledger_id_by_ledger_pool_name( + write_ledger.pool_name + ) return web.json_response({"ledger_id": ledger_id}) +@docs(tags=["ledger"], summary="Set write ledger") +@match_info_schema(WriteLedgerRequestSchema()) +@response_schema(WriteLedgerSchema, 200, description="") +async def set_write_ledger(request: web.BaseRequest): + """ + Request handler for setting write ledger. + + Args: + request: aiohttp request object + + Returns: + The set write ledger identifier + + """ + context: AdminRequestContext = request["context"] + async with context.profile.session() as session: + multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) + if not multiledger_mgr: + reason = "Multiple ledger support not enabled" + raise web.HTTPForbidden(reason=reason) + req_ledger_id = request.match_info.get("ledger_id") + try: + set_ledger_id = await multiledger_mgr.set_profile_write_ledger( + ledger_id=req_ledger_id, + profile=context.profile, + ) + except MultipleLedgerManagerError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + return web.json_response({"write_ledger": set_ledger_id}) + + @docs( tags=["ledger"], summary="Fetch the multiple ledger configuration currently in use" ) @@ -740,6 +811,12 @@ async def register(app: web.Application): web.get( "/ledger/multiple/get-write-ledger", get_write_ledger, allow_head=False ), + web.put("/ledger/multiple/{ledger_id}/set-write-ledger", set_write_ledger), + web.get( + "/ledger/multiple/get-write-ledgers", + get_write_ledgers, + allow_head=False, + ), web.get("/ledger/multiple/config", get_ledger_config, allow_head=False), ] ) diff --git a/aries_cloudagent/revocation/routes.py b/aries_cloudagent/revocation/routes.py index 729b4876a9..6afd43e70c 100644 --- a/aries_cloudagent/revocation/routes.py +++ b/aries_cloudagent/revocation/routes.py @@ -825,9 +825,11 @@ async def update_rev_reg_revoked_state(request: web.BaseRequest): genesis_transactions = context.settings.get("ledger.genesis_transactions") if not genesis_transactions: ledger_manager = context.injector.inject(BaseMultipleLedgerManager) - write_ledgers = await ledger_manager.get_write_ledger() - LOGGER.debug(f"write_ledgers = {write_ledgers}") - pool = write_ledgers[1].pool + write_ledger = context.injector.inject(BaseLedger) + available_write_ledgers = await ledger_manager.get_write_ledgers() + LOGGER.debug(f"available write_ledgers = {available_write_ledgers}") + LOGGER.debug(f"write_ledger = {write_ledger}") + pool = write_ledger[1].pool LOGGER.debug(f"write_ledger pool = {pool}") genesis_transactions = pool.genesis_txns diff --git a/aries_cloudagent/tails/indy_tails_server.py b/aries_cloudagent/tails/indy_tails_server.py index 0c5ebb6ab4..04b9ab9b78 100644 --- a/aries_cloudagent/tails/indy_tails_server.py +++ b/aries_cloudagent/tails/indy_tails_server.py @@ -5,6 +5,7 @@ from typing import Tuple from ..config.injection_context import InjectionContext +from ..ledger.base import BaseLedger from ..ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from ..utils.http import put_file, PutError @@ -42,9 +43,11 @@ async def upload_tails_file( if not genesis_transactions: ledger_manager = context.injector.inject(BaseMultipleLedgerManager) - write_ledgers = await ledger_manager.get_write_ledger() - LOGGER.debug(f"write_ledgers = {write_ledgers}") - pool = write_ledgers[1].pool + write_ledger = context.injector.inject(BaseLedger) + available_write_ledgers = await ledger_manager.get_write_ledgers() + LOGGER.debug(f"available write_ledgers = {available_write_ledgers}") + LOGGER.debug(f"write_ledger = {write_ledger}") + pool = write_ledger[1].pool LOGGER.debug(f"write_ledger pool = {pool}") genesis_transactions = pool.genesis_txns From 63671697b37d997325da36a0cba80371a59b95dd Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 24 Jul 2023 09:01:32 -0700 Subject: [PATCH 2/8] additional changes Signed-off-by: Shaanjot Gill --- Multiledger.md | 8 +++- aries_cloudagent/askar/profile.py | 45 +++++++++++++++++- aries_cloudagent/config/argparse.py | 38 ++++++++------- .../tests/test-ledger-args-no-genesis.yaml | 7 +++ .../tests/test-ledger-args-no-write.yaml | 32 +++++++++++++ .../config/tests/test-ledger-args.yaml | 2 + .../config/tests/test_argparse.py | 31 ++++++++++++- aries_cloudagent/config/tests/test_ledger.py | 11 ++--- aries_cloudagent/indy/sdk/profile.py | 46 ++++++++++++++++++- .../indy/sdk/tests/test_profile.py | 9 +++- .../ledger/multiple_ledger/indy_manager.py | 22 +++------ .../multiple_ledger/indy_vdr_manager.py | 22 +++------ .../multiple_ledger/manager_provider.py | 13 ------ .../tests/test_indy_manager.py | 35 ++++++++++++-- .../tests/test_indy_vdr_manager.py | 36 +++++++++++++-- aries_cloudagent/ledger/tests/test_routes.py | 4 +- aries_cloudagent/tails/tests/test_indy.py | 27 ++++++----- 17 files changed, 282 insertions(+), 106 deletions(-) create mode 100644 aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml create mode 100644 aries_cloudagent/config/tests/test-ledger-args-no-write.yaml diff --git a/Multiledger.md b/Multiledger.md index 5d575c9969..c71b1dcbe9 100644 --- a/Multiledger.md +++ b/Multiledger.md @@ -1,6 +1,6 @@ # Multi-ledger in ACA-Py -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). @@ -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 @@ -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 diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index a8cb10df71..450bb055f3 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -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)) ) diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index acfc1f0a52..eea818fbe2 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -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 @@ -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): diff --git a/aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml b/aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml new file mode 100644 index 0000000000..acb41e1f67 --- /dev/null +++ b/aries_cloudagent/config/tests/test-ledger-args-no-genesis.yaml @@ -0,0 +1,7 @@ +- id: sovrinMain + is_production: true + is_write: true +- id: sovrinStaging + is_production: true +- id: sovrinTest + is_production: false \ No newline at end of file diff --git a/aries_cloudagent/config/tests/test-ledger-args-no-write.yaml b/aries_cloudagent/config/tests/test-ledger-args-no-write.yaml new file mode 100644 index 0000000000..894a0e1784 --- /dev/null +++ b/aries_cloudagent/config/tests/test-ledger-args-no-write.yaml @@ -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' \ No newline at end of file diff --git a/aries_cloudagent/config/tests/test-ledger-args.yaml b/aries_cloudagent/config/tests/test-ledger-args.yaml index 894a0e1784..2e37278ee8 100644 --- a/aries_cloudagent/config/tests/test-ledger-args.yaml +++ b/aries_cloudagent/config/tests/test-ledger-args.yaml @@ -1,5 +1,6 @@ - id: sovrinMain is_production: true + is_write: true genesis_transactions: reqSignature: {} txn: @@ -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 diff --git a/aries_cloudagent/config/tests/test_argparse.py b/aries_cloudagent/config/tests/test_argparse.py index a439fd0220..3554c8b793 100644 --- a/aries_cloudagent/config/tests/test_argparse.py +++ b/aries_cloudagent/config/tests/test_argparse.py @@ -67,15 +67,39 @@ 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 @@ -83,7 +107,9 @@ async def test_get_genesis_transactions_list_with_ledger_selection(self): { "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 ( @@ -91,6 +117,7 @@ async def test_get_genesis_transactions_list_with_ledger_selection(self): "id": "sovrinTest", "is_production": False, "genesis_url": "http://localhost:9000/genesis", + "pool_name": "sovrinTest", } ) in settings.get("ledger.ledger_config_list") diff --git a/aries_cloudagent/config/tests/test_ledger.py b/aries_cloudagent/config/tests/test_ledger.py index f9c4175f03..7ccfff9bf3 100644 --- a/aries_cloudagent/config/tests/test_ledger.py +++ b/aries_cloudagent/config/tests/test_ledger.py @@ -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": { @@ -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, @@ -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 = { diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index a8bba88f7a..1174a5a511 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -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 @@ -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)) ) diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index 8047d4cea6..2441527d79 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -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) diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 2e7a1a7c0d..7395cea102 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -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( @@ -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, diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py index f96f6e90c4..8125ea5f37 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py @@ -68,21 +68,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_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: """Return BaseLedger instance.""" @@ -92,10 +86,8 @@ async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: 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( diff --git a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py index 08181cc759..918e3abbd9 100644 --- a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py @@ -9,7 +9,6 @@ from ...config.settings import BaseSettings from ...config.injector import BaseInjector, InjectionError from ...core.profile import Profile -from ...ledger.base import BaseLedger from ...utils.classloader import ClassNotFoundError, DeferLoad from .base_manager import MultipleLedgerManagerError @@ -105,12 +104,6 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): indy_sdk_non_production_ledgers[ ledger_id ] = ledger_instance - if settings.get_value("ledger.genesis_transactions"): - ledger_instance = self.root_profile.inject_or(BaseLedger) - ledger_id = ledger_instance.pool.name - indy_sdk_production_ledgers[ledger_id] = ledger_instance - write_ledgers.add(ledger_id) - indy_sdk_production_ledgers.move_to_end(ledger_id, last=False) self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_sdk_production_ledgers, @@ -150,12 +143,6 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): indy_vdr_production_ledgers[ledger_id] = ledger_instance else: indy_vdr_non_production_ledgers[ledger_id] = ledger_instance - if settings.get_value("ledger.genesis_transactions"): - ledger_instance = self.root_profile.inject_or(BaseLedger) - ledger_id = ledger_instance.pool.name - indy_vdr_production_ledgers[ledger_id] = ledger_instance - write_ledgers.add(ledger_id) - indy_vdr_production_ledgers.move_to_end(ledger_id, last=False) self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_vdr_production_ledgers, diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py index fc3f972999..ea82b1d946 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py @@ -36,7 +36,7 @@ async def setUp(self): test_prod_ledger = IndySdkLedger( IndySdkLedgerPool("test_prod_1", checked=True), self.profile ) - test_write_ledger = ("test_prod_1", test_prod_ledger) + writable_ledgers = set() self.context.injector.bind_instance(BaseLedger, test_prod_ledger) self.production_ledger["test_prod_1"] = test_prod_ledger self.production_ledger["test_prod_2"] = IndySdkLedger( @@ -48,17 +48,42 @@ async def setUp(self): self.non_production_ledger["test_non_prod_2"] = IndySdkLedger( IndySdkLedgerPool("test_non_prod_2", checked=True), self.profile ) + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") self.manager = MultiIndyLedgerManager( self.profile, production_ledgers=self.production_ledger, non_production_ledgers=self.non_production_ledger, - write_ledger_info=test_write_ledger, + writable_ledgers=writable_ledgers, ) - async def test_get_write_ledger(self): - ledger_id, ledger_inst = await self.manager.get_write_ledger() + async def test_get_write_ledgers(self): + ledger_ids = await self.manager.get_write_ledgers() + assert "test_prod_1" in ledger_ids + assert "test_prod_2" in ledger_ids + + async def test_get_write_ledger_from_base_ledger(self): + ledger_id = await self.manager.get_ledger_id_by_ledger_pool_name("test_prod_1") assert ledger_id == "test_prod_1" - assert ledger_inst.pool.name == "test_prod_1" + + async def test_set_profile_write_ledger(self): + profile = InMemoryProfile.test_profile() + assert not profile.inject_or(BaseLedger) + assert "test_prod_2" in self.manager.writable_ledgers + new_write_ledger_id = await self.manager.set_profile_write_ledger( + profile=profile, ledger_id="test_prod_2" + ) + assert new_write_ledger_id == "test_prod_2" + new_write_ledger = profile.inject_or(BaseLedger) + assert new_write_ledger.pool_name == "test_prod_2" + + async def test_set_profile_write_ledger_x(self): + profile = InMemoryProfile.test_profile() + with self.assertRaises(MultipleLedgerManagerError) as cm: + new_write_ledger_id = await self.manager.set_profile_write_ledger( + profile=profile, ledger_id="test_non_prod_1" + ) + assert "is not write configurable" in str(cm.exception.message) async def test_get_ledger_inst_by_id(self): ledger_inst = await self.manager.get_ledger_inst_by_id("test_prod_2") diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py index 4c4798750d..2a7efb379d 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py @@ -11,6 +11,7 @@ from ....cache.base import BaseCache from ....cache.in_memory import InMemoryCache from ....core.in_memory import InMemoryProfile +from ....ledger.base import BaseLedger from ....messaging.responder import BaseResponder from ...error import LedgerError @@ -65,7 +66,7 @@ async def setUp(self): self.production_ledger = OrderedDict() self.non_production_ledger = OrderedDict() test_prod_ledger = IndyVdrLedger(IndyVdrLedgerPool("test_prod_1"), self.profile) - test_write_ledger = ("test_prod_1", test_prod_ledger) + writable_ledgers = set() self.production_ledger["test_prod_1"] = test_prod_ledger self.production_ledger["test_prod_2"] = IndyVdrLedger( IndyVdrLedgerPool("test_prod_2"), self.profile @@ -76,17 +77,42 @@ async def setUp(self): self.non_production_ledger["test_non_prod_2"] = IndyVdrLedger( IndyVdrLedgerPool("test_non_prod_2"), self.profile ) + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") self.manager = MultiIndyVDRLedgerManager( self.profile, production_ledgers=self.production_ledger, non_production_ledgers=self.non_production_ledger, - write_ledger_info=test_write_ledger, + writable_ledgers=writable_ledgers, ) - async def test_get_write_ledger(self): - ledger_id, ledger_inst = await self.manager.get_write_ledger() + async def test_get_write_ledgers(self): + ledger_ids = await self.manager.get_write_ledgers() + assert "test_prod_1" in ledger_ids + assert "test_prod_2" in ledger_ids + + async def test_get_write_ledger_from_base_ledger(self): + ledger_id = await self.manager.get_ledger_id_by_ledger_pool_name("test_prod_1") assert ledger_id == "test_prod_1" - assert ledger_inst.pool.name == "test_prod_1" + + async def test_set_profile_write_ledger(self): + profile = InMemoryProfile.test_profile() + assert not profile.inject_or(BaseLedger) + assert "test_prod_2" in self.manager.writable_ledgers + new_write_ledger_id = await self.manager.set_profile_write_ledger( + profile=profile, ledger_id="test_prod_2" + ) + assert new_write_ledger_id == "test_prod_2" + new_write_ledger = profile.inject_or(BaseLedger) + assert new_write_ledger.pool_name == "test_prod_2" + + async def test_set_profile_write_ledger_x(self): + profile = InMemoryProfile.test_profile() + with self.assertRaises(MultipleLedgerManagerError) as cm: + new_write_ledger_id = await self.manager.set_profile_write_ledger( + profile=profile, ledger_id="test_non_prod_1" + ) + assert "is not write configurable" in str(cm.exception.message) async def test_get_ledger_inst_by_id(self): ledger_inst = await self.manager.get_ledger_inst_by_id("test_prod_2") diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index 14853fc6bb..f475d9ec10 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -777,8 +777,8 @@ async def test_get_write_ledger(self): self.profile.context.injector.bind_instance( BaseMultipleLedgerManager, async_mock.MagicMock( - get_write_ledger=async_mock.AsyncMock( - return_value=("test_ledger_id", self.ledger) + get_ledger_id_by_ledger_pool_name=async_mock.AsyncMock( + return_value="test_ledger_id" ) ), ) diff --git a/aries_cloudagent/tails/tests/test_indy.py b/aries_cloudagent/tails/tests/test_indy.py index 65d026a59e..082d772955 100644 --- a/aries_cloudagent/tails/tests/test_indy.py +++ b/aries_cloudagent/tails/tests/test_indy.py @@ -2,6 +2,7 @@ from ...config.injection_context import InjectionContext from ...core.in_memory import InMemoryProfile +from ...ledger.base import BaseLedger from ...ledger.multiple_ledger.base_manager import BaseMultipleLedgerManager from .. import indy_tails_server as test_module @@ -48,16 +49,15 @@ async def test_upload_indy_sdk(self): profile.context.injector.bind_instance( BaseMultipleLedgerManager, async_mock.MagicMock( - get_write_ledger=async_mock.CoroutineMock( - return_value=( - "test_ledger_id", - async_mock.MagicMock( - pool=async_mock.MagicMock(genesis_transactions="dummy") - ), - ) + get_write_ledgers=async_mock.CoroutineMock( + return_value=[ + "test_ledger_id_1", + "test_ledger_id_2", + ] ) ), ) + profile.context.injector.bind_instance(BaseLedger, async_mock.MagicMock()) indy_tails = test_module.IndyTailsServer() with async_mock.patch.object( @@ -80,16 +80,15 @@ async def test_upload_indy_vdr(self): profile.context.injector.bind_instance( BaseMultipleLedgerManager, async_mock.MagicMock( - get_write_ledger=async_mock.CoroutineMock( - return_value=( - "test_ledger_id", - async_mock.MagicMock( - pool=async_mock.MagicMock(genesis_txns_cache="dummy") - ), - ) + get_write_ledgers=async_mock.CoroutineMock( + return_value=[ + "test_ledger_id_1", + "test_ledger_id_2", + ] ) ), ) + profile.context.injector.bind_instance(BaseLedger, async_mock.MagicMock()) indy_tails = test_module.IndyTailsServer() with async_mock.patch.object( From f7966e5fe12bbbf1159cd20e4fc9f81a92ae45e6 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 24 Jul 2023 14:15:20 -0700 Subject: [PATCH 3/8] doc + endpoint update Signed-off-by: Shaanjot Gill --- Multiledger.md | 43 +++++++++++++--- aries_cloudagent/ledger/routes.py | 19 +++---- aries_cloudagent/ledger/tests/test_routes.py | 15 ++++-- open-api/openapi.json | 54 +++++++++++++++++++- open-api/swagger.json | 54 +++++++++++++++++++- 5 files changed, 159 insertions(+), 26 deletions(-) diff --git a/Multiledger.md b/Multiledger.md index c71b1dcbe9..2862c1da1e 100644 --- a/Multiledger.md +++ b/Multiledger.md @@ -36,6 +36,37 @@ If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-fi genesis_url: 'http://test.bcovrin.vonx.io/genesis' ``` +``` +- id: localVON + is_production: false + genesis_url: 'http://host.docker.internal:9000/genesis' +- id: bcorvinTest + is_production: true + is_write: true + genesis_url: 'http://test.bcovrin.vonx.io/genesis' +- id: greenlightDev + is_production: true + is_write: true + genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' +``` + +Note: `is_write` property means that the ledger is write configurable. With reference to the above config example, both `bcorvinTest` and `greenlightDev` ledgers are write configurable. By default, on startup `bcorvinTest` will be the write ledger as it is the topmost write configurable production ledger, [more details](#write-requests) regarding the selection rule. Using `PUT /ledger/{ledger_id}/set-write-ledger` endpoint, either `greenlightDev` and `bcorvinTest` can be set as the write ledger. + +``` +- id: localVON + is_production: false + is_write: true + genesis_url: 'http://host.docker.internal:9000/genesis' +- id: bcorvinTest + is_production: true + genesis_url: 'http://test.bcovrin.vonx.io/genesis' +- id: greenlightDev + is_production: true + genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' +``` + +Note: For instance with regards to example config above, `localVON` will be the write ledger, as there are no production ledgers which are configurable it will choose the topmost write configurable non production ledger. + ### Config properties For each ledger, the required properties are as following: @@ -52,20 +83,20 @@ 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 this ledger is writable/can be write configurable. Multiple write ledgers can be specified in config. +- `is_write`: Whether this ledger is writable. It requires atleast one write ledger specified. Multiple write ledgers can be specified in config. ## Multi-ledger Admin API -Multi-ledger related actions are grouped under the `ledger` topic in the SwaggerUI or under `/ledger/multiple` path. +Multi-ledger related actions are grouped under the `ledger` topic in the SwaggerUI. -- `/ledger/multiple/config`: +- GET `/ledger/config`: Returns the multiple ledger configuration currently in use -- `/ledger/multiple/get-write-ledger`: +- GET `/ledger/get-write-ledger`: Returns the current active/set `write_ledger's` `ledger_id` -- `/ledger/multiple/get-write-ledgers`: +- GET `/ledger/get-write-ledgers`: Returns list of available `write_ledger's` `ledger_id` -- `/ledger/multiple/{ledger_id}/set-write-ledger`: +- PUT `/ledger/{ledger_id}/set-write-ledger`: Set active `write_ledger's` `ledger_id` ## Ledger Selection diff --git a/aries_cloudagent/ledger/routes.py b/aries_cloudagent/ledger/routes.py index a2e4544d0a..0087129a86 100644 --- a/aries_cloudagent/ledger/routes.py +++ b/aries_cloudagent/ledger/routes.py @@ -683,8 +683,7 @@ async def get_write_ledgers(request: web.BaseRequest): async with context.profile.session() as session: multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) if not multiledger_mgr: - reason = "Multiple ledger support not enabled" - raise web.HTTPForbidden(reason=reason) + return web.json_response(["default"]) available_write_ledgers = await multiledger_mgr.get_write_ledgers() return web.json_response(available_write_ledgers) @@ -707,8 +706,7 @@ async def get_write_ledger(request: web.BaseRequest): multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) write_ledger = session.inject(BaseLedger) if not multiledger_mgr: - reason = "Multiple ledger support not enabled" - raise web.HTTPForbidden(reason=reason) + return web.json_response({"ledger_id": "default"}) ledger_id = await multiledger_mgr.get_ledger_id_by_ledger_pool_name( write_ledger.pool_name ) @@ -733,8 +731,7 @@ async def set_write_ledger(request: web.BaseRequest): async with context.profile.session() as session: multiledger_mgr = session.inject_or(BaseMultipleLedgerManager) if not multiledger_mgr: - reason = "Multiple ledger support not enabled" - raise web.HTTPForbidden(reason=reason) + return web.json_response({"write_ledger": "default"}) req_ledger_id = request.match_info.get("ledger_id") try: set_ledger_id = await multiledger_mgr.set_profile_write_ledger( @@ -808,16 +805,14 @@ async def register(app: web.Application): web.get("/ledger/did-endpoint", get_did_endpoint, allow_head=False), web.get("/ledger/taa", ledger_get_taa, allow_head=False), web.post("/ledger/taa/accept", ledger_accept_taa), + web.get("/ledger/get-write-ledger", get_write_ledger, allow_head=False), + web.put("/ledger/{ledger_id}/set-write-ledger", set_write_ledger), web.get( - "/ledger/multiple/get-write-ledger", get_write_ledger, allow_head=False - ), - web.put("/ledger/multiple/{ledger_id}/set-write-ledger", set_write_ledger), - web.get( - "/ledger/multiple/get-write-ledgers", + "/ledger/get-write-ledgers", get_write_ledgers, allow_head=False, ), - web.get("/ledger/multiple/config", get_ledger_config, allow_head=False), + web.get("/ledger/config", get_ledger_config, allow_head=False), ] ) diff --git a/aries_cloudagent/ledger/tests/test_routes.py b/aries_cloudagent/ledger/tests/test_routes.py index f475d9ec10..0cf7b164a9 100644 --- a/aries_cloudagent/ledger/tests/test_routes.py +++ b/aries_cloudagent/ledger/tests/test_routes.py @@ -793,10 +793,17 @@ async def test_get_write_ledger(self): ) assert result is json_response.return_value - async def test_get_write_ledger_x(self): - with self.assertRaises(test_module.web.HTTPForbidden) as cm: - await test_module.get_write_ledger(self.request) - assert "No instance provided for BaseMultipleLedgerManager" in cm + async def test_get_write_ledger_single_ledger(self): + with async_mock.patch.object( + test_module.web, "json_response", async_mock.Mock() + ) as json_response: + result = await test_module.get_write_ledger(self.request) + json_response.assert_called_once_with( + { + "ledger_id": "default", + } + ) + assert result is json_response.return_value async def test_get_ledger_config(self): self.profile.context.injector.bind_instance( diff --git a/open-api/openapi.json b/open-api/openapi.json index 9e48752bde..dedbb454c0 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2773,7 +2773,7 @@ "tags" : [ "ledger" ] } }, - "/ledger/multiple/config" : { + "/ledger/config" : { "get" : { "responses" : { "200" : { @@ -2791,7 +2791,49 @@ "tags" : [ "ledger" ] } }, - "/ledger/multiple/get-write-ledger" : { + "/ledger/get-write-ledgers" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Fetch list of available write ledger", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "type" : "array", + "items" : { + "type" : "string", + "example" : "ledger id" + } + } + } + } + }, + "/ledger/set-write-ledger" : { + "put" : { + "tags" : [ "ledger" ], + "summary" : "Set write ledger", + "produces" : [ "application/json" ], + "parameters" : [ + { + "name" : "ledger_id", + "in" : "path", + "description" : "Ledger identifier", + "required" : true, + "type" : "string" + } + ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WriteLedgerResponse" + } + } + } + } + }, + "/ledger/get-write-ledger" : { "get" : { "responses" : { "200" : { @@ -13023,6 +13065,14 @@ }, "type" : "object" }, + "WriteLedgerResponse" : { + "type" : "object", + "properties" : { + "write_ledger" : { + "type" : "string" + } + } + }, "UpdateProfileSettingsRequest" : { "type" : "object", "properties" : { diff --git a/open-api/swagger.json b/open-api/swagger.json index bbf53925e8..15b7a5f531 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -2276,7 +2276,7 @@ } } }, - "/ledger/multiple/config" : { + "/ledger/config" : { "get" : { "tags" : [ "ledger" ], "summary" : "Fetch the multiple ledger configuration currently in use", @@ -2292,7 +2292,49 @@ } } }, - "/ledger/multiple/get-write-ledger" : { + "/ledger/get-write-ledgers" : { + "get" : { + "tags" : [ "ledger" ], + "summary" : "Fetch list of available write ledger", + "produces" : [ "application/json" ], + "parameters" : [ ], + "responses" : { + "200" : { + "description" : "", + "type" : "array", + "items" : { + "type" : "string", + "example" : "ledger id" + } + } + } + } + }, + "/ledger/set-write-ledger" : { + "put" : { + "tags" : [ "ledger" ], + "summary" : "Set write ledger", + "produces" : [ "application/json" ], + "parameters" : [ + { + "name" : "ledger_id", + "in" : "path", + "description" : "Ledger identifier", + "required" : true, + "type" : "string" + } + ], + "responses" : { + "200" : { + "description" : "", + "schema" : { + "$ref" : "#/definitions/WriteLedgerResponse" + } + } + } + } + }, + "/ledger/get-write-ledger" : { "get" : { "tags" : [ "ledger" ], "summary" : "Fetch the current write ledger", @@ -11949,6 +11991,14 @@ } } }, + "WriteLedgerResponse" : { + "type" : "object", + "properties" : { + "write_ledger" : { + "type" : "string" + } + } + }, "UpdateProfileSettingsRequest" : { "type" : "object", "properties" : { From f9bb910a39a93418cfd86a933c29460112629262 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 27 Jul 2023 06:54:55 -0700 Subject: [PATCH 4/8] endorser info in multi ledger config + tests Signed-off-by: Shaanjot Gill --- aries_cloudagent/askar/profile.py | 10 ++++ aries_cloudagent/askar/tests/test_profile.py | 36 +++++++++++++ aries_cloudagent/config/ledger.py | 35 +++++++------ aries_cloudagent/config/tests/test_ledger.py | 4 ++ aries_cloudagent/indy/sdk/profile.py | 10 ++++ .../indy/sdk/tests/test_profile.py | 37 ++++++++++++++ .../ledger/multiple_ledger/base_manager.py | 4 ++ .../ledger/multiple_ledger/indy_manager.py | 14 +++++ .../multiple_ledger/indy_vdr_manager.py | 14 +++++ .../multiple_ledger/manager_provider.py | 51 ++++++++++++------- .../tests/test_indy_manager.py | 47 ++++++++++++++++- .../tests/test_indy_vdr_manager.py | 47 ++++++++++++++++- .../tests/test_manager_provider.py | 2 + 13 files changed, 273 insertions(+), 38 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 450bb055f3..90f8ace5be 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -162,6 +162,16 @@ def bind_providers(self): ref(self), ), ) + if ( + "endorser_alias" in write_ledger_config + and "endorser_did" in write_ledger_config + ): + self.settings["endorser.endorser_alias"] = write_ledger_config.get( + "endorser_alias" + ) + self.settings["endorser.endorser_public_did"] = write_ledger_config.get( + "endorser_did" + ) elif self.ledger_pool: injector.bind_provider( BaseLedger, ClassProvider(IndyVdrLedger, self.ledger_pool, ref(self)) diff --git a/aries_cloudagent/askar/tests/test_profile.py b/aries_cloudagent/askar/tests/test_profile.py index aef94fa481..842e627cd8 100644 --- a/aries_cloudagent/askar/tests/test_profile.py +++ b/aries_cloudagent/askar/tests/test_profile.py @@ -6,6 +6,7 @@ from ...askar.profile import AskarProfile from ...config.injection_context import InjectionContext +from ...ledger.base import BaseLedger from .. import profile as test_module @@ -24,6 +25,41 @@ async def test_init_success(open_store): assert askar_profile.opened == open_store +@pytest.mark.asyncio +async def test_init_multi_ledger(open_store): + context = InjectionContext( + settings={ + "ledger.ledger_config_list": [ + { + "id": "BCovrinDev", + "is_production": True, + "is_write": True, + "endorser_did": "9QPa6tHvBHttLg6U4xvviv", + "endorser_alias": "endorser_dev", + "genesis_transactions": mock.MagicMock(), + }, + { + "id": "SovrinStagingNet", + "is_production": False, + "genesis_transactions": mock.MagicMock(), + }, + ] + } + ) + askar_profile = AskarProfile( + open_store, + context=context, + ) + + assert askar_profile.opened == open_store + assert askar_profile.settings["endorser.endorser_alias"] == "endorser_dev" + assert ( + askar_profile.settings["endorser.endorser_public_did"] + == "9QPa6tHvBHttLg6U4xvviv" + ) + assert (askar_profile.inject_or(BaseLedger)).pool_name == "BCovrinDev" + + @pytest.mark.asyncio async def test_remove_success(open_store): openStore = open_store diff --git a/aries_cloudagent/config/ledger.py b/aries_cloudagent/config/ledger.py index 03574d6521..750ba314ff 100644 --- a/aries_cloudagent/config/ledger.py +++ b/aries_cloudagent/config/ledger.py @@ -91,22 +91,25 @@ async def load_multiple_genesis_transactions_from_config(settings: Settings): ledger_id = config.get("id") or str(uuid.uuid4()) if is_write_ledger: write_ledger_set = True - ledger_txns_list.append( - { - "id": ledger_id, - "is_production": ( - True - if config.get("is_production") is None - else config.get("is_production") - ), - "is_write": is_write_ledger, - "genesis_transactions": txns, - "keepalive": int(config.get("keepalive", 5)), - "read_only": bool(config.get("read_only", False)), - "socks_proxy": config.get("socks_proxy"), - "pool_name": config.get("pool_name", ledger_id), - } - ) + config_item = { + "id": ledger_id, + "is_production": ( + True + if config.get("is_production") is None + else config.get("is_production") + ), + "is_write": is_write_ledger, + "genesis_transactions": txns, + "keepalive": int(config.get("keepalive", 5)), + "read_only": bool(config.get("read_only", False)), + "socks_proxy": config.get("socks_proxy"), + "pool_name": config.get("pool_name", ledger_id), + } + if "endorser_alias" in config: + config_item["endorser_alias"] = config.get("endorser_alias") + if "endorser_did" in config: + config_item["endorser_did"] = config.get("endorser_did") + ledger_txns_list.append(config_item) if not write_ledger_set and not ( settings.get("ledger.genesis_transactions") or settings.get("ledger.genesis_file") diff --git a/aries_cloudagent/config/tests/test_ledger.py b/aries_cloudagent/config/tests/test_ledger.py index 7ccfff9bf3..8d72dfded6 100644 --- a/aries_cloudagent/config/tests/test_ledger.py +++ b/aries_cloudagent/config/tests/test_ledger.py @@ -293,6 +293,8 @@ async def test_load_multiple_genesis_transactions_from_config_a(self): "read_only": False, "socks_proxy": None, "pool_name": "sovrinMain", + "endorser_did": "9QPa6tHvBHttLg6U4xvviv", + "endorser_alias": "endorser_main", }, { "id": "sovrinStaging", @@ -322,6 +324,8 @@ async def test_load_multiple_genesis_transactions_from_config_a(self): "is_production": True, "is_write": True, "genesis_transactions": TEST_GENESIS_TXNS, + "endorser_did": "9QPa6tHvBHttLg6U4xvviv", + "endorser_alias": "endorser_main", }, { "id": "sovrinStaging", diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index 1174a5a511..0a23209b21 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -130,6 +130,16 @@ def bind_providers(self): ref(self), ), ) + if ( + "endorser_alias" in write_ledger_config + and "endorser_did" in write_ledger_config + ): + self.settings["endorser.endorser_alias"] = write_ledger_config.get( + "endorser_alias" + ) + self.settings["endorser.endorser_public_did"] = write_ledger_config.get( + "endorser_did" + ) elif self.ledger_pool: injector.bind_provider( BaseLedger, ClassProvider(IndySdkLedger, self.ledger_pool, ref(self)) diff --git a/aries_cloudagent/indy/sdk/tests/test_profile.py b/aries_cloudagent/indy/sdk/tests/test_profile.py index 2441527d79..8ebd62dfb9 100644 --- a/aries_cloudagent/indy/sdk/tests/test_profile.py +++ b/aries_cloudagent/indy/sdk/tests/test_profile.py @@ -6,7 +6,9 @@ from ....config.injection_context import InjectionContext from ....core.error import ProfileError +from ....ledger.base import BaseLedger from ....ledger.indy import IndySdkLedgerPool + from ..profile import IndySdkProfile from ..wallet_setup import IndyOpenWallet, IndyWalletConfig @@ -35,6 +37,41 @@ async def profile(open_wallet): profile._finalizer() +@pytest.mark.asyncio +async def test_init_multi_ledger(open_wallet): + context = InjectionContext( + settings={ + "ledger.ledger_config_list": [ + { + "id": "BCovrinDev", + "is_production": True, + "is_write": True, + "endorser_did": "9QPa6tHvBHttLg6U4xvviv", + "endorser_alias": "endorser_dev", + "genesis_transactions": async_mock.MagicMock(), + }, + { + "id": "SovrinStagingNet", + "is_production": False, + "genesis_transactions": async_mock.MagicMock(), + }, + ] + } + ) + askar_profile = IndySdkProfile( + open_wallet, + context=context, + ) + + assert askar_profile.opened == open_wallet + assert askar_profile.settings["endorser.endorser_alias"] == "endorser_dev" + assert ( + askar_profile.settings["endorser.endorser_public_did"] + == "9QPa6tHvBHttLg6U4xvviv" + ) + assert (askar_profile.inject_or(BaseLedger)).pool_name == "BCovrinDev" + + @pytest.mark.asyncio async def test_properties(profile: IndySdkProfile): assert profile.name == "test-profile" diff --git a/aries_cloudagent/ledger/multiple_ledger/base_manager.py b/aries_cloudagent/ledger/multiple_ledger/base_manager.py index 78506c0d7a..8e06ff4e05 100644 --- a/aries_cloudagent/ledger/multiple_ledger/base_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/base_manager.py @@ -19,6 +19,10 @@ class BaseMultipleLedgerManager(ABC): def __init__(self, profile: Profile): """Initialize Multiple Ledger Manager.""" + @abstractmethod + def get_endorser_info_for_ledger(self, ledger_id: str) -> Optional[Tuple[str, str]]: + """Return endorser alias, did tuple for provided ledger, if available.""" + @abstractmethod async def get_write_ledgers(self) -> List[str]: """Return write ledger.""" diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 7395cea102..029461c363 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -34,6 +34,7 @@ def __init__( production_ledgers: OrderedDict = OrderedDict(), non_production_ledgers: OrderedDict = OrderedDict(), writable_ledgers: set = set(), + endorser_map: dict = {}, cache_ttl: int = None, ): """Initialize MultiIndyLedgerManager. @@ -49,6 +50,7 @@ def __init__( self.production_ledgers = production_ledgers self.non_production_ledgers = non_production_ledgers self.writable_ledgers = writable_ledgers + self.endorser_map = endorser_map self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) self.cache_ttl = cache_ttl @@ -56,6 +58,13 @@ async def get_write_ledgers(self) -> List[str]: """Return the write IndySdkLedger instance.""" return list(self.writable_ledgers) + def get_endorser_info_for_ledger(self, ledger_id: str) -> Optional[Tuple[str, str]]: + """Return endorser alias, did tuple for provided ledger, if available.""" + endorser_info = self.endorser_map.get(ledger_id) + if not endorser_info: + return None + return (endorser_info["endorser_alias"], endorser_info["endorser_did"]) + async def get_ledger_inst_by_id(self, ledger_id: str) -> Optional[BaseLedger]: """Return BaseLedger instance.""" return self.production_ledgers.get( @@ -92,6 +101,11 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st profile.context.injector.bind_instance( BaseLedger, multi_ledgers.get(ledger_id) ) + endorser_info = self.get_endorser_info_for_ledger(ledger_id) + if endorser_info: + endorser_alias, endorser_did = endorser_info + profile.context.settings["endorser.endorser_alias"] = endorser_alias + profile.context.settings["endorser.endorser_public_did"] = endorser_did return ledger_id raise MultipleLedgerManagerError(f"No ledger info found for {ledger_id}.") diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py index 8125ea5f37..f84223aed8 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py @@ -34,6 +34,7 @@ def __init__( production_ledgers: OrderedDict = OrderedDict(), non_production_ledgers: OrderedDict = OrderedDict(), writable_ledgers: set = set(), + endorser_map: dict = {}, cache_ttl: int = None, ): """Initialize MultiIndyLedgerManager. @@ -49,6 +50,7 @@ def __init__( self.production_ledgers = production_ledgers self.non_production_ledgers = non_production_ledgers self.writable_ledgers = writable_ledgers + self.endorser_map = endorser_map self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) self.cache_ttl = cache_ttl @@ -56,6 +58,13 @@ async def get_write_ledgers(self) -> List[str]: """Return the write IndyVdrLedger instance.""" return list(self.writable_ledgers) + def get_endorser_info_for_ledger(self, ledger_id: str) -> Optional[Tuple[str, str]]: + """Return endorser alias, did tuple for provided ledger, if available.""" + endorser_info = self.endorser_map.get(ledger_id) + if not endorser_info: + return None + return (endorser_info["endorser_alias"], endorser_info["endorser_did"]) + async def get_prod_ledgers(self) -> Mapping: """Return production ledgers mapping.""" return self.production_ledgers @@ -75,6 +84,11 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st profile.context.injector.bind_instance( BaseLedger, multi_ledgers.get(ledger_id) ) + endorser_info = self.get_endorser_info_for_ledger(ledger_id) + if endorser_info: + endorser_alias, endorser_did = endorser_info + profile.context.settings["endorser.endorser_alias"] = endorser_alias + profile.context.settings["endorser.endorser_public_did"] = endorser_did return ledger_id raise MultipleLedgerManagerError(f"No ledger info found for {ledger_id}.") diff --git a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py index 918e3abbd9..3a6799034f 100644 --- a/aries_cloudagent/ledger/multiple_ledger/manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/manager_provider.py @@ -72,6 +72,7 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): indy_sdk_production_ledgers = OrderedDict() indy_sdk_non_production_ledgers = OrderedDict() ledger_config_list = settings.get_value("ledger.ledger_config_list") + ledger_endorser_map = {} write_ledgers = set() for config in ledger_config_list: keepalive = config.get("keepalive") @@ -83,37 +84,43 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): pool_name = config.get("pool_name") ledger_is_production = config.get("is_production") ledger_is_write = config.get("is_write") + ledger_endorser_alias = config.get("endorser_alias") + ledger_endorser_did = config.get("endorser_did") + ledger_pool = pool_class( + pool_name, + keepalive=keepalive, + cache=cache, + genesis_transactions=genesis_transactions, + read_only=read_only, + socks_proxy=socks_proxy, + ) + ledger_instance = ledger_class( + pool=ledger_pool, + profile=self.root_profile, + ) if ledger_is_write: write_ledgers.add(ledger_id) + if ledger_is_production: + indy_sdk_production_ledgers[ledger_id] = ledger_instance else: - ledger_pool = pool_class( - pool_name, - keepalive=keepalive, - cache=cache, - genesis_transactions=genesis_transactions, - read_only=read_only, - socks_proxy=socks_proxy, - ) - ledger_instance = ledger_class( - pool=ledger_pool, - profile=self.root_profile, - ) - if ledger_is_production: - indy_sdk_production_ledgers[ledger_id] = ledger_instance - else: - indy_sdk_non_production_ledgers[ - ledger_id - ] = ledger_instance + indy_sdk_non_production_ledgers[ledger_id] = ledger_instance + if ledger_endorser_alias and ledger_endorser_did: + ledger_endorser_map[ledger_id] = { + "endorser_alias": ledger_endorser_alias, + "endorser_did": ledger_endorser_did, + } self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_sdk_production_ledgers, non_production_ledgers=indy_sdk_non_production_ledgers, writable_ledgers=write_ledgers, + endorser_map=ledger_endorser_map, ) else: indy_vdr_production_ledgers = OrderedDict() indy_vdr_non_production_ledgers = OrderedDict() ledger_config_list = settings.get_value("ledger.ledger_config_list") + ledger_endorser_map = {} write_ledgers = set() for config in ledger_config_list: keepalive = config.get("keepalive") @@ -125,6 +132,8 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): pool_name = config.get("pool_name") ledger_is_production = config.get("is_production") ledger_is_write = config.get("is_write") + ledger_endorser_alias = config.get("endorser_alias") + ledger_endorser_did = config.get("endorser_did") ledger_pool = pool_class( pool_name, keepalive=keepalive, @@ -143,11 +152,17 @@ def provide(self, settings: BaseSettings, injector: BaseInjector): indy_vdr_production_ledgers[ledger_id] = ledger_instance else: indy_vdr_non_production_ledgers[ledger_id] = ledger_instance + if ledger_endorser_alias and ledger_endorser_did: + ledger_endorser_map[ledger_id] = { + "endorser_alias": ledger_endorser_alias, + "endorser_did": ledger_endorser_did, + } self._inst[manager_type] = manager_class( self.root_profile, production_ledgers=indy_vdr_production_ledgers, non_production_ledgers=indy_vdr_non_production_ledgers, writable_ledgers=write_ledgers, + endorser_map=ledger_endorser_map, ) except ClassNotFoundError as err: raise InjectionError( diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py index ea82b1d946..ba384b561d 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_manager.py @@ -57,6 +57,34 @@ async def setUp(self): writable_ledgers=writable_ledgers, ) + def test_get_endorser_info_for_ledger(self): + writable_ledgers = set() + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") + + endorser_info_map = {} + endorser_info_map["test_prod_1"] = { + "endorser_did": "test_public_did_1", + "endorser_alias": "endorser_1", + } + endorser_info_map["test_prod_2"] = { + "endorser_did": "test_public_did_2", + "endorser_alias": "endorser_2", + } + manager = MultiIndyLedgerManager( + self.profile, + production_ledgers=self.production_ledger, + non_production_ledgers=self.non_production_ledger, + writable_ledgers=writable_ledgers, + endorser_map=endorser_info_map, + ) + assert ( + "endorser_1" + ), "test_public_did_1" == manager.get_endorser_info_for_ledger("test_prod_1") + assert ( + "endorser_2" + ), "test_public_did_2" == manager.get_endorser_info_for_ledger("test_prod_2") + async def test_get_write_ledgers(self): ledger_ids = await self.manager.get_write_ledgers() assert "test_prod_1" in ledger_ids @@ -67,10 +95,25 @@ async def test_get_write_ledger_from_base_ledger(self): assert ledger_id == "test_prod_1" async def test_set_profile_write_ledger(self): + writable_ledgers = set() + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") + endorser_info_map = {} + endorser_info_map["test_prod_2"] = { + "endorser_did": "test_public_did_2", + "endorser_alias": "endorser_2", + } + manager = MultiIndyLedgerManager( + self.profile, + production_ledgers=self.production_ledger, + non_production_ledgers=self.non_production_ledger, + writable_ledgers=writable_ledgers, + endorser_map=endorser_info_map, + ) profile = InMemoryProfile.test_profile() assert not profile.inject_or(BaseLedger) - assert "test_prod_2" in self.manager.writable_ledgers - new_write_ledger_id = await self.manager.set_profile_write_ledger( + assert "test_prod_2" in manager.writable_ledgers + new_write_ledger_id = await manager.set_profile_write_ledger( profile=profile, ledger_id="test_prod_2" ) assert new_write_ledger_id == "test_prod_2" diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py index 2a7efb379d..b61813db8e 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_indy_vdr_manager.py @@ -86,6 +86,34 @@ async def setUp(self): writable_ledgers=writable_ledgers, ) + def test_get_endorser_info_for_ledger(self): + writable_ledgers = set() + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") + + endorser_info_map = {} + endorser_info_map["test_prod_1"] = { + "endorser_did": "test_public_did_1", + "endorser_alias": "endorser_1", + } + endorser_info_map["test_prod_2"] = { + "endorser_did": "test_public_did_2", + "endorser_alias": "endorser_2", + } + manager = MultiIndyVDRLedgerManager( + self.profile, + production_ledgers=self.production_ledger, + non_production_ledgers=self.non_production_ledger, + writable_ledgers=writable_ledgers, + endorser_map=endorser_info_map, + ) + assert ( + "endorser_1" + ), "test_public_did_1" == manager.get_endorser_info_for_ledger("test_prod_1") + assert ( + "endorser_2" + ), "test_public_did_2" == manager.get_endorser_info_for_ledger("test_prod_2") + async def test_get_write_ledgers(self): ledger_ids = await self.manager.get_write_ledgers() assert "test_prod_1" in ledger_ids @@ -96,10 +124,25 @@ async def test_get_write_ledger_from_base_ledger(self): assert ledger_id == "test_prod_1" async def test_set_profile_write_ledger(self): + writable_ledgers = set() + writable_ledgers.add("test_prod_1") + writable_ledgers.add("test_prod_2") + endorser_info_map = {} + endorser_info_map["test_prod_2"] = { + "endorser_did": "test_public_did_2", + "endorser_alias": "endorser_2", + } + manager = MultiIndyVDRLedgerManager( + self.profile, + production_ledgers=self.production_ledger, + non_production_ledgers=self.non_production_ledger, + writable_ledgers=writable_ledgers, + endorser_map=endorser_info_map, + ) profile = InMemoryProfile.test_profile() assert not profile.inject_or(BaseLedger) - assert "test_prod_2" in self.manager.writable_ledgers - new_write_ledger_id = await self.manager.set_profile_write_ledger( + assert "test_prod_2" in manager.writable_ledgers + new_write_ledger_id = await manager.set_profile_write_ledger( profile=profile, ledger_id="test_prod_2" ) assert new_write_ledger_id == "test_prod_2" diff --git a/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py b/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py index 14c75c514b..9adb82e5ad 100644 --- a/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py +++ b/aries_cloudagent/ledger/multiple_ledger/tests/test_manager_provider.py @@ -45,6 +45,8 @@ "is_production": True, "is_write": True, "genesis_transactions": TEST_GENESIS_TXN, + "endorser_did": "public_staging_endorser_did", + "endorser_alias": "endorser_staging", }, { "id": "sovrinTest", From 8d3daa7575ac254e61aad7380c8ed51655b536ea Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 27 Jul 2023 07:18:05 -0700 Subject: [PATCH 5/8] doc update Signed-off-by: Shaanjot Gill --- Multiledger.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Multiledger.md b/Multiledger.md index 2862c1da1e..41a4606e43 100644 --- a/Multiledger.md +++ b/Multiledger.md @@ -44,6 +44,8 @@ If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-fi is_production: true is_write: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' + endorser_did: '9QPa6tHvBHttLg6U4xvviv' + endorser_alias: 'endorser_test' - id: greenlightDev is_production: true is_write: true @@ -84,6 +86,10 @@ Optional properties: - `keepalive`: how many seconds to keep the ledger open - `socks_proxy` - `is_write`: Whether this ledger is writable. It requires atleast one write ledger specified. Multiple write ledgers can be specified in config. +- `endorser_did`: Endorser public DID registered on the ledger, needed for supporting Endorser protocol at multi-ledger level. +- `endorser_alias`: Endorser alias for this ledger, needed for supporting Endorser protocol at multi-ledger level. + +Note: Both `endorser_did` and `endorser_alias` are part of the endorser info. Whenever a write ledger is selected using `PUT /ledger/{ledger_id}/set-write-ledger`, the endorser info associated with that ledger in the config updates the `endorser.endorser_public_did` and `endorser.endorser_alias` profile setting respectively. ## Multi-ledger Admin API From bcb1e4d672159bf8ff5488620dd00e44868bf91e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 28 Jul 2023 09:01:55 -0700 Subject: [PATCH 6/8] extract common profile code into ml utility Signed-off-by: Shaanjot Gill --- aries_cloudagent/askar/profile.py | 23 ++++------------------- aries_cloudagent/indy/sdk/profile.py | 23 ++++------------------- aries_cloudagent/utils/multi_ledger.py | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 38 deletions(-) create mode 100644 aries_cloudagent/utils/multi_ledger.py diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 90f8ace5be..20d964987d 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -23,6 +23,7 @@ from ..ledger.indy_vdr import IndyVdrLedger, IndyVdrLedgerPool from ..storage.base import BaseStorage, BaseStorageSearch from ..storage.vc_holder.base import VCHolder +from ..utils.multi_ledger import get_write_ledger_config_for_profile from ..wallet.base import BaseWallet from ..wallet.crypto import validate_seed @@ -124,26 +125,10 @@ def bind_providers(self): 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) + write_ledger_config = get_write_ledger_config_for_profile( + settings=self.settings + ) 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( diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index 0a23209b21..cdebc86018 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -15,6 +15,7 @@ from ...ledger.indy import IndySdkLedger, IndySdkLedgerPool from ...storage.base import BaseStorage, BaseStorageSearch from ...storage.vc_holder.base import VCHolder +from ...utils.multi_ledger import get_write_ledger_config_for_profile from ...wallet.base import BaseWallet from ...wallet.indy import IndySdkWallet from ..holder import IndyHolder @@ -92,26 +93,10 @@ def bind_providers(self): 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) + write_ledger_config = get_write_ledger_config_for_profile( + settings=self.settings + ) 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( diff --git a/aries_cloudagent/utils/multi_ledger.py b/aries_cloudagent/utils/multi_ledger.py new file mode 100644 index 0000000000..cad7290def --- /dev/null +++ b/aries_cloudagent/utils/multi_ledger.py @@ -0,0 +1,26 @@ +"""Multiledger related utility methods.""" + +from ..core.error import ProfileError +from ..config.settings import BaseSettings + + +def get_write_ledger_config_for_profile(settings: BaseSettings) -> dict: + """Return initial/default write ledger config on profile creation.""" + write_ledger_config = None + prod_write_ledger_pool = [] + non_prod_write_ledger_pool = [] + for ledger_config in 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) + 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" + ) + return write_ledger_config From 4f1637b08e878dfb0f5ece6fb1a5bd68fd5409b5 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Fri, 28 Jul 2023 10:52:27 -0700 Subject: [PATCH 7/8] bcovrin typo fix Signed-off-by: Shaanjot Gill --- Multiledger.md | 10 +++++----- demo/multi_ledger_config.yml | 2 +- demo/multi_ledger_config_bdd.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Multiledger.md b/Multiledger.md index 41a4606e43..823da2ef31 100644 --- a/Multiledger.md +++ b/Multiledger.md @@ -30,7 +30,7 @@ If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-fi - id: localVON is_production: false genesis_url: 'http://host.docker.internal:9000/genesis' -- id: bcorvinTest +- id: bcovrinTest is_production: true is_write: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' @@ -40,7 +40,7 @@ If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-fi - id: localVON is_production: false genesis_url: 'http://host.docker.internal:9000/genesis' -- id: bcorvinTest +- id: bcovrinTest is_production: true is_write: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' @@ -52,14 +52,14 @@ If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-fi genesis_url: 'http://dev.greenlight.bcovrin.vonx.io/genesis' ``` -Note: `is_write` property means that the ledger is write configurable. With reference to the above config example, both `bcorvinTest` and `greenlightDev` ledgers are write configurable. By default, on startup `bcorvinTest` will be the write ledger as it is the topmost write configurable production ledger, [more details](#write-requests) regarding the selection rule. Using `PUT /ledger/{ledger_id}/set-write-ledger` endpoint, either `greenlightDev` and `bcorvinTest` can be set as the write ledger. +Note: `is_write` property means that the ledger is write configurable. With reference to the above config example, both `bcovrinTest` and `greenlightDev` ledgers are write configurable. By default, on startup `bcovrinTest` will be the write ledger as it is the topmost write configurable production ledger, [more details](#write-requests) regarding the selection rule. Using `PUT /ledger/{ledger_id}/set-write-ledger` endpoint, either `greenlightDev` and `bcovrinTest` can be set as the write ledger. ``` - id: localVON is_production: false is_write: true genesis_url: 'http://host.docker.internal:9000/genesis' -- id: bcorvinTest +- id: bcovrinTest is_production: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' - id: greenlightDev @@ -144,7 +144,7 @@ If multiple ledgers are configured then `IndyLedgerRequestsExecutor` service ext ### Write Requests -On startup, the first configured applicable ledger is assigned as the `write_ledger` [`BaseLedger`], the selection is dependant on the order (top-down) and whether it is `production` or `non_production`. For instance, considering this [example configuration](#example-config-file), ledger `bcorvinTest` will be set as `write_ledger` as it is the topmost `production` ledger. If no `production` ledgers are included in configuration then the topmost `non_production` ledger is selected. +On startup, the first configured applicable ledger is assigned as the `write_ledger` [`BaseLedger`], the selection is dependant on the order (top-down) and whether it is `production` or `non_production`. For instance, considering this [example configuration](#example-config-file), ledger `bcovrinTest` will be set as `write_ledger` as it is the topmost `production` ledger. If no `production` ledgers are included in configuration then the topmost `non_production` ledger is selected. ## A Special Warning for TAA Acceptance diff --git a/demo/multi_ledger_config.yml b/demo/multi_ledger_config.yml index 2c423b8d51..c66cc0f7a2 100644 --- a/demo/multi_ledger_config.yml +++ b/demo/multi_ledger_config.yml @@ -1,7 +1,7 @@ #- id: local # is_production: true # genesis_url: 'http://$LEDGER_HOST:9000/genesis' -- id: bcorvinTest +- id: bcovrinTest is_production: true is_write: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' diff --git a/demo/multi_ledger_config_bdd.yml b/demo/multi_ledger_config_bdd.yml index 14f7919fe1..883c2a52c6 100644 --- a/demo/multi_ledger_config_bdd.yml +++ b/demo/multi_ledger_config_bdd.yml @@ -2,7 +2,7 @@ is_production: true is_write: true genesis_url: 'http://$LEDGER_HOST:9000/genesis' -- id: bcorvinTest +- id: bcovrinTest is_production: true # is_write: true genesis_url: 'http://test.bcovrin.vonx.io/genesis' From 7580a3500a79f8e2ee059984f61821075fb6fbcb Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 31 Jul 2023 13:16:17 -0700 Subject: [PATCH 8/8] write ldger persistence in multitenant mode Signed-off-by: Shaanjot Gill --- aries_cloudagent/askar/profile.py | 1 + aries_cloudagent/indy/sdk/profile.py | 1 + .../ledger/multiple_ledger/indy_manager.py | 14 ++++++ .../multiple_ledger/indy_vdr_manager.py | 14 ++++++ aries_cloudagent/utils/multi_ledger.py | 44 ++++++++++++++----- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/aries_cloudagent/askar/profile.py b/aries_cloudagent/askar/profile.py index 20d964987d..42a8b5c68d 100644 --- a/aries_cloudagent/askar/profile.py +++ b/aries_cloudagent/askar/profile.py @@ -147,6 +147,7 @@ def bind_providers(self): ref(self), ), ) + self.settings["ledger.write_ledger"] = write_ledger_config.get("id") if ( "endorser_alias" in write_ledger_config and "endorser_did" in write_ledger_config diff --git a/aries_cloudagent/indy/sdk/profile.py b/aries_cloudagent/indy/sdk/profile.py index cdebc86018..1ea02c9051 100644 --- a/aries_cloudagent/indy/sdk/profile.py +++ b/aries_cloudagent/indy/sdk/profile.py @@ -115,6 +115,7 @@ def bind_providers(self): ref(self), ), ) + self.settings["ledger.write_ledger"] = write_ledger_config.get("id") if ( "endorser_alias" in write_ledger_config and "endorser_did" in write_ledger_config diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py index 029461c363..97f787a74e 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_manager.py @@ -11,6 +11,7 @@ from ...core.profile import Profile from ...ledger.base import BaseLedger from ...ledger.error import LedgerError +from ...multitenant.manager import BaseMultitenantManager from ...wallet.crypto import did_is_self_certified from ..indy import IndySdkLedger @@ -96,6 +97,8 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st raise MultipleLedgerManagerError( f"Provided Ledger identifier {ledger_id} is not write configurable." ) + extra_settings = {} + multi_tenant_mgr = self.profile.inject_or(BaseMultitenantManager) multi_ledgers = self.production_ledgers | self.non_production_ledgers if ledger_id in multi_ledgers: profile.context.injector.bind_instance( @@ -106,6 +109,17 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st endorser_alias, endorser_did = endorser_info profile.context.settings["endorser.endorser_alias"] = endorser_alias profile.context.settings["endorser.endorser_public_did"] = endorser_did + profile.context.settings["ledger.write_ledger"] = ledger_id + if multi_tenant_mgr: + extra_settings = { + "endorser.endorser_alias": endorser_alias, + "endorser.endorser_public_did": endorser_did, + "ledger.write_ledger": ledger_id, + } + await multi_tenant_mgr.update_wallet( + profile.context.settings["wallet.id"], + extra_settings, + ) return ledger_id raise MultipleLedgerManagerError(f"No ledger info found for {ledger_id}.") diff --git a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py index f84223aed8..1ee24598d3 100644 --- a/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py +++ b/aries_cloudagent/ledger/multiple_ledger/indy_vdr_manager.py @@ -11,6 +11,7 @@ from ...core.profile import Profile from ...ledger.base import BaseLedger from ...ledger.error import LedgerError +from ...multitenant.manager import BaseMultitenantManager from ...wallet.crypto import did_is_self_certified from ..indy_vdr import IndyVdrLedger @@ -79,6 +80,8 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st raise MultipleLedgerManagerError( f"Provided Ledger identifier {ledger_id} is not write configurable." ) + extra_settings = {} + multi_tenant_mgr = self.profile.inject_or(BaseMultitenantManager) multi_ledgers = self.production_ledgers | self.non_production_ledgers if ledger_id in multi_ledgers: profile.context.injector.bind_instance( @@ -89,6 +92,17 @@ async def set_profile_write_ledger(self, ledger_id: str, profile: Profile) -> st endorser_alias, endorser_did = endorser_info profile.context.settings["endorser.endorser_alias"] = endorser_alias profile.context.settings["endorser.endorser_public_did"] = endorser_did + profile.context.settings["ledger.write_ledger"] = ledger_id + if multi_tenant_mgr: + extra_settings = { + "endorser.endorser_alias": endorser_alias, + "endorser.endorser_public_did": endorser_did, + "ledger.write_ledger": ledger_id, + } + await multi_tenant_mgr.update_wallet( + profile.context.settings["wallet.id"], + extra_settings, + ) return ledger_id raise MultipleLedgerManagerError(f"No ledger info found for {ledger_id}.") diff --git a/aries_cloudagent/utils/multi_ledger.py b/aries_cloudagent/utils/multi_ledger.py index cad7290def..fc241bd8ac 100644 --- a/aries_cloudagent/utils/multi_ledger.py +++ b/aries_cloudagent/utils/multi_ledger.py @@ -1,4 +1,5 @@ """Multiledger related utility methods.""" +from collections import OrderedDict from ..core.error import ProfileError from ..config.settings import BaseSettings @@ -7,20 +8,39 @@ def get_write_ledger_config_for_profile(settings: BaseSettings) -> dict: """Return initial/default write ledger config on profile creation.""" write_ledger_config = None - prod_write_ledger_pool = [] - non_prod_write_ledger_pool = [] + prod_write_ledger_pool = OrderedDict() + non_prod_write_ledger_pool = OrderedDict() for ledger_config in 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) + prod_write_ledger_pool[ + ledger_config.get("id") or ledger_config.get("pool_name") + ] = ledger_config elif not ledger_config.get("is_production") and ledger_config.get("is_write"): - non_prod_write_ledger_pool.append(ledger_config) - 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] + non_prod_write_ledger_pool[ + ledger_config.get("id") or ledger_config.get("pool_name") + ] = ledger_config + if "ledger.write_ledger" in settings: + if settings.get("ledger.write_ledger") in prod_write_ledger_pool: + write_ledger_config = prod_write_ledger_pool.get( + settings.get("ledger.write_ledger") + ) + elif settings.get("ledger.write_ledger") in non_prod_write_ledger_pool: + write_ledger_config = non_prod_write_ledger_pool.get( + settings.get("ledger.write_ledger") + ) + else: + raise ProfileError( + "The ledger.write_ledger in profile settings does not correspond to a" + " write configurable ledger provided with --genesis-transactions-list" + ) else: - raise ProfileError( - "No write ledger configuration found in ledger_config_list which " - "was provided with --genesis-transactions-list" - ) + if len(prod_write_ledger_pool) >= 1: + write_ledger_config = (list(prod_write_ledger_pool.values()))[0] + elif len(non_prod_write_ledger_pool) >= 1: + write_ledger_config = (list(non_prod_write_ledger_pool.values()))[0] + else: + raise ProfileError( + "No write ledger configuration found in ledger_config_list which " + "was provided with --genesis-transactions-list" + ) return write_ledger_config