Skip to content

Commit d2dbf5c

Browse files
authored
Merge branch 'main' into fix/conn-id-in-keylist-webhook
2 parents 25b6ec6 + 98f5537 commit d2dbf5c

43 files changed

Lines changed: 543 additions & 270 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pip-audit.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ jobs:
1616
run: |
1717
python -m venv env/
1818
source env/bin/activate
19+
python -m pip install --upgrade pip
1920
python -m pip install .
20-
- uses: trailofbits/gh-action-pip-audit@v0.0.4
21+
- uses: pypa/gh-action-pip-audit@v1.0.0
2122
with:
2223
virtual-environment: env/
2324
local: true

.github/workflows/sonarcloud.yml

Lines changed: 0 additions & 41 deletions
This file was deleted.

DIDResolution.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ In practice, DIDs and DID Documents are used for a variety of purposes but espec
2929

3030
## `DIDResolver`
3131

32-
In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup through the `DIDResolverRegistry`. This registry enables additional resolvers to be loaded via plugin.
32+
In ACA-Py, the `DIDResolver` provides the interface to resolve DIDs using registered method resolvers. Method resolver registration happens on startup in a `did_resolvers` list. This registry enables additional resolvers to be loaded via plugin.
3333

3434
#### Example usage:
3535
```python=
@@ -73,17 +73,17 @@ The following is an example method resolver implementation. In this example, we
7373

7474
```python=
7575
from aries_cloudagent.config.injection_context import InjectionContext
76-
from aries_cloudagent.resolver.did_resolver_registry import DIDResolverRegistry
76+
from ..resolver.did_resolver import DIDResolver
7777
7878
from .example_resolver import ExampleResolver
7979
8080
8181
async def setup(context: InjectionContext):
8282
"""Setup the plugin."""
83-
registry = context.inject(DIDResolverRegistry)
83+
registry = context.inject(DIDResolver)
8484
resolver = ExampleResolver()
8585
await resolver.setup(context)
86-
registry.register(resolver)
86+
registry.append(resolver)
8787
```
8888

8989
#### `example_resolver.py`

Endorser.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Transaction Endorser Support
22

3-
Note that the ACA-Py transaction support is in the process of code refactor and cleanup. The following documents the current state, but is subject to change.
4-
53
ACA-Py supports an [Endorser Protocol](https://github.com/hyperledger/aries-rfcs/pull/586), that allows an un-privileged agent (an "Author") to request another agent (the "Endorser") to sign their transactions so they can write these transactions to the ledger. This is required on Indy ledgers, where new agents will typically be granted only "Author" privileges.
64

75
Transaction Endorsement is built into the protocols for Schema, Credential Definition and Revocation, and endorsements can be explicitely requested, or ACA-Py can be configured to automate the endorsement workflow.
@@ -61,3 +59,44 @@ Endorsement:
6159
For Authors, specify whether to automatically promote a DID to the wallet public DID after writing to the ledger.
6260
```
6361

62+
## How Aca-py Handles Endorsements
63+
64+
Internally, the Endorsement functionality is implemented as a protocol, and is implemented consistently with other protocols:
65+
66+
- a [routes.py](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py) file exposes the admin endpoints
67+
- [handler files](https://github.com/hyperledger/aries-cloudagent-python/tree/main/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers) implement responses to any received Endorse protocol messages
68+
- a [manager.py](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py) file implements common functionality that is called from both the routes.py and handler classes (as well as from other classes that need to interact with Endorser functionality)
69+
70+
The Endorser makes use of the [Event Bus](https://github.com/hyperledger/aries-cloudagent-python/blob/main/CHANGELOG.md#july-14-2021) (links to the PR which links to a hackmd doc) to notify other protocols of any Endorser events of interest. For example, after a Credential Definition endorsement is received, the TransactionManager writes the endorsed transaction to the ledger and uses the Event Bus to notify the Credential Defintition manager that it can do any required post-processing (such as writing the cred def record to the wallet, initiating the revocation registry, etc.).
71+
72+
The overall architecture can be illustrated as:
73+
74+
![Class Diagram](./docs/assets/endorser-design.png)
75+
76+
### Create Credential Definition and Revocation Registry
77+
78+
An example of an Endorser flow is as follows, showing how a credential definition endorsement is received and processed, and optionally kicks off the revocation registry process:
79+
80+
![Sequence Diagram](./docs/assets/endorse-cred-def.png)
81+
82+
You can see that there is a standard endorser flow happening each time there is a ledger write (illustrated in the "Endorser" process).
83+
84+
At the end of each endorse sequence, the TransactionManager sends a notification via the EventBus so that any dependant processing can continue. Each Router is responsible for listening and responding to these notifications if necessary.
85+
86+
For example:
87+
88+
- Once the credential definition is created, a revocation registry must be created (for revocable cred defs)
89+
- Once the revocation registry is created, a revocation entry must be created
90+
- Potentially, the cred def status could be updated once the revocation entry is completed
91+
92+
Using the EventBus decouples the event sequence. Any functions triggered by an event notification are typically also available directly via Admin endpoints.
93+
94+
### Create DID and Promote to Public
95+
96+
... and an example of creating a DID and promoting it to public (and creating an ATTRIB for the endpoint:
97+
98+
![Sequence Diagram](./docs/assets/endorse-public-did.png)
99+
100+
You can see the same endorsement processes in this sequence.
101+
102+
Once the DID is written, the DID can (optionally) be promoted to the public DID, which will also invoke an ATTRIB transaction to write the endpoint.

aries_cloudagent/config/default_context.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from ..core.protocol_registry import ProtocolRegistry
1313
from ..core.goal_code_registry import GoalCodeRegistry
1414
from ..resolver.did_resolver import DIDResolver
15-
from ..resolver.did_resolver_registry import DIDResolverRegistry
1615
from ..tails.base import BaseTailsServer
1716

1817
from ..protocols.actionmenu.v1_0.base_service import BaseMenuService
@@ -50,12 +49,8 @@ async def build_context(self) -> InjectionContext:
5049
# Global event bus
5150
context.injector.bind_instance(EventBus, EventBus())
5251

53-
# Global did resolver registry
54-
did_resolver_registry = DIDResolverRegistry()
55-
context.injector.bind_instance(DIDResolverRegistry, did_resolver_registry)
56-
5752
# Global did resolver
58-
context.injector.bind_instance(DIDResolver, DIDResolver(did_resolver_registry))
53+
context.injector.bind_instance(DIDResolver, DIDResolver([]))
5954

6055
await self.bind_providers(context)
6156
await self.load_plugins(context)

aries_cloudagent/core/tests/test_conductor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from ...protocols.coordinate_mediation.v1_0.models.mediation_record import (
2525
MediationRecord,
2626
)
27-
from ...resolver.did_resolver import DIDResolver, DIDResolverRegistry
27+
from ...resolver.did_resolver import DIDResolver
2828
from ...multitenant.base import BaseMultitenantManager
2929
from ...multitenant.manager import MultitenantManager
3030
from ...storage.base import BaseStorage
@@ -92,7 +92,7 @@ async def build_context(self) -> InjectionContext:
9292
context.injector.bind_instance(ProfileManager, InMemoryProfileManager())
9393
context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry())
9494
context.injector.bind_instance(BaseWireFormat, self.wire_format)
95-
context.injector.bind_instance(DIDResolver, DIDResolver(DIDResolverRegistry()))
95+
context.injector.bind_instance(DIDResolver, DIDResolver([]))
9696
context.injector.bind_instance(EventBus, MockEventBus())
9797
return context
9898

aries_cloudagent/holder/routes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ async def credentials_remove(request: web.BaseRequest):
274274
async with context.profile.session() as session:
275275
holder = session.inject(IndyHolder)
276276
await holder.delete_credential(credential_id)
277+
topic = "acapy::record::credential::delete"
278+
await context.profile.notify(topic, {"id": credential_id, "state": "deleted"})
277279
except WalletNotFoundError as err:
278280
raise web.HTTPNotFound(reason=err.roll_up) from err
279281

@@ -376,6 +378,10 @@ async def w3c_cred_remove(request: web.BaseRequest):
376378
try:
377379
vc_record = await holder.retrieve_credential_by_id(credential_id)
378380
await holder.delete_credential(vc_record)
381+
topic = "acapy::record::w3c_credential::delete"
382+
await session.profile.notify(
383+
topic, {"id": credential_id, "state": "deleted"}
384+
)
379385
except StorageNotFoundError as err:
380386
raise web.HTTPNotFound(reason=err.roll_up) from err
381387
except StorageError as err:

aries_cloudagent/ledger/base.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,15 @@ async def _construct_attr_json(
9898
if not routing_keys:
9999
routing_keys = []
100100

101-
endpoint_dict = {"endpoint": endpoint}
102-
103101
if all_exist_endpoints:
104-
all_exist_endpoints[endpoint_type.indy] = endpoint_dict
105-
endpoint_dict["routingKeys"] = routing_keys
102+
all_exist_endpoints[endpoint_type.indy] = endpoint
103+
all_exist_endpoints["routingKeys"] = routing_keys
106104
attr_json = json.dumps({"endpoint": all_exist_endpoints})
107105

108106
else:
109-
endpoint_val = {endpoint_type.indy: endpoint_dict}
107+
endpoint_dict = {endpoint_type.indy: endpoint}
110108
endpoint_dict["routingKeys"] = routing_keys
111-
attr_json = json.dumps({"endpoint": endpoint_val})
109+
attr_json = json.dumps({"endpoint": endpoint_dict})
112110

113111
return attr_json
114112

aries_cloudagent/ledger/indy.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ async def update_endpoint_for_did(
793793
)
794794
resp = await self._submit(
795795
request_json,
796-
True,
796+
sign=True,
797797
sign_did=public_info,
798798
write_ledger=write_ledger,
799799
)
@@ -802,6 +802,7 @@ async def update_endpoint_for_did(
802802

803803
await self._submit(request_json, True, True)
804804
return True
805+
805806
return False
806807

807808
async def register_nym(

aries_cloudagent/ledger/tests/test_indy.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,17 +2308,37 @@ async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open
23082308
attr_json = await ledger._construct_attr_json(
23092309
"https://url",
23102310
EndpointType.ENDPOINT,
2311-
all_exist_endpoints={"Endpoint": "https://endpoint"},
23122311
routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
23132312
)
23142313
assert attr_json == json.dumps(
23152314
{
23162315
"endpoint": {
2317-
"Endpoint": "https://endpoint",
2318-
"endpoint": {
2319-
"endpoint": "https://url",
2320-
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
2321-
},
2316+
"endpoint": "https://url",
2317+
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
2318+
}
2319+
}
2320+
)
2321+
2322+
@async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open")
2323+
@async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close")
2324+
@pytest.mark.asyncio
2325+
async def test_construct_attr_json_with_routing_keys_all_exist_endpoints(
2326+
self, mock_close, mock_open
2327+
):
2328+
ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile)
2329+
async with ledger:
2330+
attr_json = await ledger._construct_attr_json(
2331+
"https://url",
2332+
EndpointType.ENDPOINT,
2333+
all_exist_endpoints={"profile": "https://endpoint/profile"},
2334+
routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
2335+
)
2336+
assert attr_json == json.dumps(
2337+
{
2338+
"endpoint": {
2339+
"profile": "https://endpoint/profile",
2340+
"endpoint": "https://url",
2341+
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
23222342
}
23232343
}
23242344
)

0 commit comments

Comments
 (0)