Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Indy Ledger support and State Proof verification #1425

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
53d0ddf
state proof validation and leaf inclusion
shaangill025 Sep 27, 2021
fc17f83
wip commit after redesign
shaangill025 Oct 4, 2021
a3531e5
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 4, 2021
8dd47ce
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 8, 2021
6fe670d
updates
shaangill025 Oct 13, 2021
84b3650
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 13, 2021
1270483
updates
shaangill025 Oct 15, 2021
a082c15
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 15, 2021
3507dd9
routes
shaangill025 Oct 15, 2021
b4c997b
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 15, 2021
2f577e6
cleanup
shaangill025 Oct 16, 2021
712a214
cleanup
shaangill025 Oct 16, 2021
89880b0
test coverage and other updates
shaangill025 Oct 19, 2021
94c361f
add get_indy_storage back to IndySdkLedger
shaangill025 Oct 19, 2021
7bca488
get_indy_storage test update
shaangill025 Oct 19, 2021
f12a0b7
merge main branch
shaangill025 Oct 28, 2021
5d2967c
resolve conflicts
shaangill025 Oct 28, 2021
74363a1
multiple ledger tests
shaangill025 Oct 29, 2021
4798b32
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Oct 29, 2021
e8aa5b1
fixed incorrect - resolved merged conflicts
shaangill025 Oct 29, 2021
8618f00
ledger route tests
shaangill025 Oct 29, 2021
ebe4853
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 1, 2021
87533bb
multiple ledger demo
shaangill025 Nov 9, 2021
69a7f88
resolve conflicts
shaangill025 Nov 9, 2021
c82cf7f
test update
shaangill025 Nov 9, 2021
70b8dad
Merge branch 'mult_indy_network_support' of https://github.com/shaang…
shaangill025 Nov 9, 2021
6b1b9de
fix and refactor of get_txn
shaangill025 Nov 9, 2021
a4537ff
reverting get_txn refactor
shaangill025 Nov 9, 2021
2a9d3c5
fixes
shaangill025 Nov 10, 2021
02933c7
doc updates
shaangill025 Nov 10, 2021
c92e100
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 10, 2021
325566c
removing dynamic allocation of ledger config
shaangill025 Nov 10, 2021
9efee68
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 10, 2021
fda6920
Merge branch 'mult_indy_network_support' of https://github.com/shaang…
shaangill025 Nov 10, 2021
126b095
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 12, 2021
cd6c9be
inject_or update (PR1494 check) and resolve conflicts
shaangill025 Nov 17, 2021
9235058
Merge branch 'mult_indy_network_support' of https://github.com/shaang…
shaangill025 Nov 17, 2021
cd95397
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 19, 2021
acb8249
Merge branch 'main' into mult_indy_network_support
ianco Nov 23, 2021
56143e0
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 23, 2021
0cb1a6c
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 23, 2021
b8d7aad
merge main into mult_indy_network_support
shaangill025 Nov 25, 2021
a3d35d2
Merge branch 'mult_indy_network_support' of https://github.com/shaang…
shaangill025 Nov 25, 2021
994fe37
updates
shaangill025 Nov 26, 2021
7771676
demo update
shaangill025 Nov 26, 2021
dec21e7
format fix
shaangill025 Nov 26, 2021
a8204f5
updates
shaangill025 Nov 29, 2021
b589974
retrigger checks
shaangill025 Nov 29, 2021
8d4c38a
Fix faber demo to run in multi-ledger mode
ianco Nov 29, 2021
4f1516b
Fix ledger check
ianco Nov 29, 2021
ce44d5c
Merge pull request #1 from ianco/master
shaangill025 Nov 29, 2021
bff4f61
Merge branch 'main' into mult_indy_network_support
shaangill025 Nov 29, 2021
b149386
Merge branch 'main' of https://github.com/hyperledger/aries-cloudagen…
shaangill025 Nov 29, 2021
32b95b7
Merge branch 'mult_indy_network_support' of https://github.com/shaang…
shaangill025 Nov 29, 2021
5bf2e47
removing unnecessary demo changes
shaangill025 Nov 29, 2021
646d377
demo change revert with
shaangill025 Nov 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions Multiledger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Multi-ledger in ACA-Py <!-- omit in toc -->

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

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

## Table of Contents <!-- omit in toc -->

- [Usage](#usage)
- [Example config file:](#example-config-file)
- [Config properties](#config-properties)
- [Multi-ledger Admin API](#multi-ledger-admin-api)
- [Ledger Selection](#ledger-selection)
- [Read Requests](#read-requests)
- [For checking ledger in parallel](#for-checking-ledger-in-parallel)
- [Write Requests](#write-requests)
- [Impact on other ACA-Py function](#impact-on-other-aca-py-function)

## Usage

Multi-ledger is disabled by default. You can enable support for multiple ledgers using the `--genesis-transactions-list` startup parameter. This parameter accepts a string which is the path to the `YAML` configuration file. For example:

`--genesis-transactions-list ./aries_cloudagent/config/multi_ledger_config.yml`

If `--genesis-transactions-list` is specified, then `--genesis-url, --genesis-file, --genesis-transactions` should not be specified.

### Example config file:
```
- 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'
```

### Config properties
For each ledger, the required properties are as following:

- `id`\*: The id (or name) of the ledger, can also be used as the pool name if none provided
- `is_production`\*: Whether the ledger is a production ledger. This is used by the pool selector algorithm to know which ledger to use for certain interactions (i.e. prefer production ledgers over non-production ledgers)

For connecting to ledger, one of the following needs to be specified:

- `genesis_file`: The path to the genesis file to use for connecting to an Indy ledger.
- `genesis_transactions`: String of genesis transactions to use for connecting to an Indy ledger.
- `genesis_url`: The url from which to download the genesis transactions to use for connecting to an Indy ledger.

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.


## Multi-ledger Admin API

Multi-ledger related actions are grouped under the `ledger` topic in the SwaggerUI or under `/ledger/multiple` path.

- `/ledger/multiple/config`:
Returns the multiple ledger configuration currently in use
- `/ledger/multiple/get-write-ledger`:
Returns the current active/set `write_ledger's` `ledger_id`

## Ledger Selection

### Read Requests

The following process is executed for these functions in ACA-Py:
1. `get_schema`
2. `get_credential_definition`
3. `get_revoc_reg_def`
4. `get_revoc_reg_entry`
5. `get_key_for_did`
6. `get_all_endpoints_for_did`
7. `get_endpoint_for_did`
8. `get_nym_role`
9. `get_revoc_reg_delta`

If multiple ledgers are configured then `IndyLedgerRequestsExecutor` service extracts `DID` from the record identifier and executes the [check](#for-checking-ledger-in-parallel) below, else it returns the `BaseLedger` instance.

#### For checking ledger in parallel

- `lookup_did_in_configured_ledgers` function
- If the calling function (above) is in [1-4], then check the `DID` in `cache` for a corresponding applicable `ledger_id`. If found, return the ledger info, else continue.
- Otherwise, launch parallel `_get_ledger_by_did` tasks for each of the configured ledgers.
- As these tasks get finished, construct `applicable_prod_ledgers` and `applicable_non_prod_ledgers` dictionaries, each with `self_certified` and `non_self_certified` inner dict which are sorted by the original order or index.
- Order/preference for selection: `self_certified` > `production` > `non_production`
- Checks `production` ledger where the `DID` is `self_certified`
- Checks `non_production` ledger where the `DID` is `self_certified`
- Checks `production` ledger where the `DID` is not `self_certified`
- Checks `non_production` ledger where the `DID` is not `self_certified`
- Return an applicable ledger if found, else raise an exception.
- `_get_ledger_by_did` function
- Build and submit `GET_NYM`
- Wait for a response for 10 seconds, if timed out return None
- Parse response
- Validate state proof
- Check if `DID` is self certified
- Returns ledger info to `lookup_did_in_configured_ledgers`

### 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.
shaangill025 marked this conversation as resolved.
Show resolved Hide resolved

## Impact on other ACA-Py function

There should be no impact/change in functionality to any ACA-Py protocols.

`IndySdkLedger` was refactored by replacing `wallet: IndySdkWallet` instance variable with `profile: Profile` and accordingly `.aries_cloudagent/indy/credex/verifier`, `.aries_cloudagent/indy/models/pres_preview`, `.aries_cloudagent/indy/sdk/profile.py`, `.aries_cloudagent/indy/sdk/verifier`, `./aries_cloudagent/indy/verifier` were also updated.

Added `build_and_return_get_nym_request` and `submit_get_nym_request` helper functions to `IndySdkLedger` and `IndyVdrLedger`.

Best practice/feedback emerging from `Askar session deadlock` issue and `endorser refactoring` PR was also addressed here by not leaving sessions open unnecessarily and changing `context.session` to `context.profile.session`, etc.

These changes are made here:
- `./aries_cloudagent/ledger/routes.py`
- `./aries_cloudagent/messaging/credential_definitions/routes.py`
- `./aries_cloudagent/messaging/schemas/routes.py`
- `./aries_cloudagent/protocols/actionmenu/v1_0/routes.py`
- `./aries_cloudagent/protocols/actionmenu/v1_0/util.py`
- `./aries_cloudagent/protocols/basicmessage/v1_0/routes.py`
- `./aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_handler.py`
- `./aries_cloudagent/protocols/coordinate_mediation/v1_0/routes.py`
- `./aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py`
- `./aries_cloudagent/protocols/introduction/v0_1/handlers/invitation_handler.py`
- `./aries_cloudagent/protocols/introduction/v0_1/routes.py`
- `./aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py`
- `./aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py`
- `./aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py`
- `./aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py`
- `./aries_cloudagent/protocols/issue_credential/v1_0/routes.py`
- `./aries_cloudagent/protocols/issue_credential/v2_0/routes.py`
- `./aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py`
- `./aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py`
- `./aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py`
- `./aries_cloudagent/protocols/present_proof/v1_0/routes.py`
- `./aries_cloudagent/protocols/trustping/v1_0/routes.py`
- `./aries_cloudagent/resolver/routes.py`
- `./aries_cloudagent/revocation/routes.py`
35 changes: 18 additions & 17 deletions aries_cloudagent/askar/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,23 @@ def init_ledger_pool(self):
if self.settings.get("ledger.disabled"):
LOGGER.info("Ledger support is disabled")
return

pool_name = self.settings.get("ledger.pool_name", "default")
keepalive = int(self.settings.get("ledger.keepalive", 5))
read_only = bool(self.settings.get("ledger.read_only", False))
socks_proxy = self.settings.get("ledger.socks_proxy")
if read_only:
LOGGER.error("Note: setting ledger to read-only mode")
genesis_transactions = self.settings.get("ledger.genesis_transactions")
cache = self.context.injector.inject_or(BaseCache)
self.ledger_pool = IndyVdrLedgerPool(
pool_name,
keepalive=keepalive,
cache=cache,
genesis_transactions=genesis_transactions,
read_only=read_only,
socks_proxy=socks_proxy,
)
if self.settings.get("ledger.genesis_transactions"):
pool_name = self.settings.get("ledger.pool_name", "default")
keepalive = int(self.settings.get("ledger.keepalive", 5))
read_only = bool(self.settings.get("ledger.read_only", False))
socks_proxy = self.settings.get("ledger.socks_proxy")
if read_only:
LOGGER.error("Note: setting ledger to read-only mode")
genesis_transactions = self.settings.get("ledger.genesis_transactions")
cache = self.context.injector.inject_or(BaseCache)
self.ledger_pool = IndyVdrLedgerPool(
pool_name,
keepalive=keepalive,
cache=cache,
genesis_transactions=genesis_transactions,
read_only=read_only,
socks_proxy=socks_proxy,
)

def bind_providers(self):
"""Initialize the profile-level instance providers."""
Expand Down Expand Up @@ -118,6 +118,7 @@ def bind_providers(self):
injector.bind_provider(
BaseLedger, ClassProvider(IndyVdrLedger, self.ledger_pool, ref(self))
)
if self.ledger_pool or self.settings.get("ledger.ledger_config_list"):
injector.bind_provider(
IndyVerifier,
ClassProvider(
Expand Down
1 change: 1 addition & 0 deletions aries_cloudagent/askar/tests/test_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async def test_remove_success(self, AskarOpenStore):
context.settings = {
"multitenant.wallet_type": "askar-profile",
"wallet.askar_profile": profile_id,
"ledger.genesis_transactions": mock.MagicMock(),
}
askar_profile = AskarProfile(openStore, context)
remove_profile_stub = asyncio.Future()
Expand Down
15 changes: 13 additions & 2 deletions aries_cloudagent/commands/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
from ..config import argparse as arg
from ..config.default_context import DefaultContextBuilder
from ..config.base import BaseError
from ..config.ledger import get_genesis_transactions, ledger_config
from ..config.ledger import (
get_genesis_transactions,
ledger_config,
load_multiple_genesis_transactions_from_config,
)
from ..config.util import common_config
from ..config.wallet import wallet_config
from ..protocols.coordinate_mediation.mediation_invite_store import (
Expand Down Expand Up @@ -36,7 +40,14 @@ async def provision(settings: dict):
context = await context_builder.build_context()

try:
await get_genesis_transactions(context.settings)
if context.settings.get("ledger.ledger_config_list"):
await load_multiple_genesis_transactions_from_config(context.settings)
if (
context.settings.get("ledger.genesis_transactions")
or context.settings.get("ledger.genesis_file")
or context.settings.get("ledger.genesis_url")
):
await get_genesis_transactions(context.settings)

root_profile, public_did = await wallet_config(context, provision=True)

Expand Down
33 changes: 29 additions & 4 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,24 +748,49 @@ def add_arguments(self, parser: ArgumentParser):
"connect to the public (outside of corporate network) ledger pool"
),
)
parser.add_argument(
"--genesis-transactions-list",
type=str,
required=False,
dest="genesis_transactions_list",
metavar="<genesis-transactions-list>",
env_var="ACAPY_GENESIS_TRANSACTIONS_LIST",
help=(
"Load YAML configuration for connecting to multiple"
" HyperLedger Indy ledgers."
),
)

def get_settings(self, args: Namespace) -> dict:
"""Extract ledger settings."""
settings = {}
if args.no_ledger:
settings["ledger.disabled"] = True
else:
configured = False
if args.genesis_url:
settings["ledger.genesis_url"] = args.genesis_url
configured = True
elif args.genesis_file:
settings["ledger.genesis_file"] = args.genesis_file
configured = True
elif args.genesis_transactions:
settings["ledger.genesis_transactions"] = args.genesis_transactions
else:
configured = True
if args.genesis_transactions_list:
with open(args.genesis_transactions_list, "r") as stream:
txn_config_list = yaml.safe_load(stream)
ledger_config_list = []
for txn_config in txn_config_list:
ledger_config_list.append(txn_config)
settings["ledger.ledger_config_list"] = ledger_config_list
configured = True
if not configured:
raise ArgsParseError(
"One of --genesis-url --genesis-file or --genesis-transactions "
"must be specified (unless --no-ledger is specified to "
"explicitly configure aca-py to run with no ledger)."
"One of --genesis-url --genesis-file, --genesis-transactions "
"or --genesis-transactions-list must be specified (unless "
"--no-ledger is specified to explicitly configure aca-py to"
" run with no ledger)."
shaangill025 marked this conversation as resolved.
Show resolved Hide resolved
)
if args.ledger_pool_name:
settings["ledger.pool_name"] = args.ledger_pool_name
Expand Down
62 changes: 62 additions & 0 deletions aries_cloudagent/config/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import re
import sys
import uuid

import markdown
import prompt_toolkit
Expand Down Expand Up @@ -56,6 +57,67 @@ async def get_genesis_transactions(settings: Settings) -> str:
return txns


async def load_multiple_genesis_transactions_from_config(settings: Settings):
"""Fetch genesis transactions for multiple ledger configuration."""

ledger_config_list = settings.get("ledger.ledger_config_list")
ledger_txns_list = []
write_ledger_set = False
for config in ledger_config_list:
txns = None
if "genesis_transactions" in config:
txns = config.get("genesis_transactions")
if not txns:
if "genesis_url" in config:
txns = await fetch_genesis_transactions(config.get("genesis_url"))
elif "genesis_file" in config:
try:
genesis_path = config.get("genesis_file")
LOGGER.info(
"Reading ledger genesis transactions from: %s", genesis_path
)
with open(genesis_path, "r") as genesis_file:
txns = genesis_file.read()
except IOError as e:
raise ConfigError(
"Error reading ledger genesis transactions"
) from e
is_write_ledger = (
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:
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),
}
)
if not write_ledger_set and not (
settings.get("ledger.genesis_transactions")
or settings.get("ledger.genesis_file")
or settings.get("ledger.genesis_url")
):
raise ConfigError(
"No is_write ledger set and no genesis_url,"
" genesis_file and genesis_transactions provided."
)
settings["ledger.ledger_config_list"] = ledger_txns_list


async def ledger_config(
profile: Profile, public_did: str, provision: bool = False
) -> bool:
Expand Down
32 changes: 32 additions & 0 deletions aries_cloudagent/config/tests/test-ledger-args.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
- id: sovrinMain
is_production: true
genesis_transactions:
reqSignature: {}
txn:
data:
data:
alias: Node1
blskey: >-
4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba
blskey_pop: >-
RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1
client_ip: 192.168.65.3
client_port: 9702
node_ip: 192.168.65.3
node_port: 9701
services:
- VALIDATOR
dest: Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv
metadata:
from: Th7MpTaRZVRYnPiabds81Y
type: '0'
txnMetadata:
seqNo: 1
txnId: fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62
ver: '1'
- id: sovrinStaging
is_production: true
genesis_file: /home/indy/ledger/sandbox/pool_transactions_genesis
- id: sovrinTest
is_production: false
genesis_url: 'http://localhost:9000/genesis'
Loading