Skip to content

Commit 0e463be

Browse files
authored
Merge branch 'main' into patch-1
2 parents 08dd1cf + 9364295 commit 0e463be

11 files changed

Lines changed: 224 additions & 16 deletions

File tree

DevReadMe.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ See the [README](README.md) for details about this repository and information ab
1818
- [Developing](#developing)
1919
- [Prerequisites](#prerequisites)
2020
- [Running Locally](#running-locally)
21+
- [Logging](#logging)
2122
- [Running Tests](#running-tests)
2223
- [Running Aries Agent Test Harness Tests](#running-aries-agent-test-harness-tests)
2324
- [Development Workflow](#development-workflow)

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ An ACA-Py instance puts together an OpenAPI-documented REST interface based on t
7878

7979
Technical note: the administrative API exposed by the agent for the controller to use must be protected with an API key (using the --admin-api-key command line arg) or deliberately left unsecured using the --admin-insecure-mode command line arg. The latter should not be used other than in development if the API is not otherwise secured.
8080

81+
## Troubleshooting
82+
83+
There are a number of resources for getting help with ACA-Py and troubleshooting
84+
any problems you might run into. The [Troubleshooting](Troubleshooting.md)
85+
document contains some guidance about issues that have been experienced in the
86+
past. Feel free to submit PRs to supplement the troubleshooting document!
87+
Searching the [ACA-Py GitHub
88+
issues](https://github.com/hyperledger/aries-cloudagent-python/issues) will
89+
often uncover challenges that others have experienced, often with answers to
90+
solving those challenges. As well, there is the "aries-cloudagent-python"
91+
channel on the Hyperledger Discord chat server ([invitation
92+
here](https://discord.gg/hyperledger)).
93+
8194
## Credit
8295

8396
The initial implementation of ACA-Py was developed by the Government of British Columbia’s Digital Trust Team in Canada. To learn more about what’s happening with decentralized identity and digital trust in British Columbia, a new website will be launching and the link will be made available here.

Troubleshooting.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Troubleshooting Aries Cloud Agent Python <!-- omit in toc -->
2+
3+
This document contains some troubleshooting information that contributors to the
4+
community think may be helpful. Most of the content here assumes the reader has
5+
gotten started with ACA-Py and has arrived here because of an issue that came up
6+
in their use of ACA-Py.
7+
8+
Contributions (via pull request) to this document are welcome. Topics added here
9+
will mostly come from reported issues that contributors think would be helpful
10+
to the larger community.
11+
12+
## Table of Contents <!-- omit in toc -->
13+
14+
- [Unable to Connect to Ledger](#unable-to-connect-to-ledger)
15+
- [Local ledger running?](#local-ledger-running)
16+
- [Any Firewalls](#any-firewalls)
17+
- [Damaged, Unpublishable Revocation Registry](#damaged-unpublishable-revocation-registry)
18+
19+
## Unable to Connect to Ledger
20+
21+
The most common issue hit by first time users is getting an error on startup "unable to connect to ledger". Here are a list of things to check when you see that error.
22+
23+
### Local ledger running?
24+
25+
Unless you specify via startup parameters or environment variables that you are using a public Hyperledger Indy ledger, ACA-Py assumes that you are running a local ledger -- an instance of [von-network](https://github.com/bcgov/von-network).
26+
If that is the cause -- have you started your local ledger, and did it startup properly. Things to check:
27+
28+
- Any errors in the startup of von-network?
29+
- Is the von-network webserver (usually at `https:/localhost:9000`) accessible? If so, can you click on and see the Genesis File?
30+
- Do you even need a local ledger? If not, you can use a public sandbox ledger,
31+
such as the [Dev Greenlight ledger](), likely by just prefacing your ACA-Py
32+
command with `LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io`. For example,
33+
when running the Alice-Faber demo in the [demo](demo) folder, you can run (for
34+
example), the Faber agent using the command:
35+
`LEDGER_URL=http://dev.greenlight.bcovrin.vonx.io ./run_demo faber`
36+
37+
### Any Firewalls
38+
39+
Do you have any firewalls in play that might be blocking the ports that are used by the ledger, notably 9701-9708? To access a ledger
40+
the ACA-Py instance must be able to get to those ports of the ledger, regardless if the ledger is local or remote.
41+
42+
## Damaged, Unpublishable Revocation Registry
43+
44+
We have discovered that in the ACA-Py AnonCreds implementation, it is possible
45+
to get into a state where the publishing of updates to a Revocation Registry
46+
(RevReg) is impossible. This can happen where ACA-Py starts to publish an update
47+
to the RevReg, but the write transaction to the Hyperledger Indy ledger fails
48+
for some reason. When a credential revocation is published, aca-py (via indy-sdk
49+
or askar/credx) updates the revocation state in the wallet as well as on the
50+
ledger. The revocation state is dependant on whatever the previous revocation
51+
state is/was, so if the ledger and wallet are mis-matched the publish will fail.
52+
(Andrew/s PR # 1804 (merged) should mitigate but probably won't completely
53+
eliminate this from happening).
54+
55+
For example, in case we've seen, the write RevRegEntry transaction failed at the
56+
ledger because there was a problem with accepting the TAA (Transaction Author
57+
Agreement). Once the error occurred, the RevReg state held by the ACA-Py agent,
58+
and the RevReg state on the ledger were different. Even after the ability to
59+
write to the ledger was restored, the RevReg could still not be published
60+
because of the differences in the RevReg state. Such a situation can now be
61+
corrected, as follows:
62+
63+
To address this issue, some new endpoints were added to ACA-Py in Release 0.7.4,
64+
as follows:
65+
66+
- GET `/revocation/registry/<id>/issued` - counts of the number of issued/revoked
67+
within a registry
68+
- GET `/revocation/registry/<id>/issued/details` - details of all credentials
69+
issued/revoked within a registry
70+
- GET `/revocation/registry/<id>/issued/indy_recs` - calculated rev_reg_delta from
71+
the ledger
72+
- This is used to compare ledger revoked vs wallet revoked credentials, which
73+
is essentially the state of the RevReg on the ledger and in ACA-Py. Where
74+
there is a difference, we have an error.
75+
- PUT `/revocation/registry/<id>/fix-revocation-entry-state` - publish an update
76+
to the RevReg state on the ledger to bring it into alignment with what is in
77+
the ACA-Py instance.
78+
- There is a boolean parameter (`apply_ledger_update`) to control whether the
79+
ledger entry actually gets published so, if you are so inclined, you can
80+
call the endpoint to see what the transaction would be, before you actually
81+
try to do a ledger update. This will return:
82+
- `rev_reg_delta` - same as the ".../indy_recs" endpoint
83+
- `accum_calculated` - transaction to write to ledger
84+
- `accum_fixed` - If `apply_ledger_update`, the transaction actually written
85+
to the ledger
86+
87+
Note that there is (currently) a backlog item to prevent the wallet and ledger
88+
from getting out of sync (e.g. don't update the ACA-Py RevReg state if the
89+
ledger write fails), but even after that change is made, having this ability
90+
will be retained for use if needed.
91+
92+
We originally ran into this due to the TAA acceptance getting lost when
93+
switching to multi-ledger (as described
94+
[here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/Multiledger.md#a-special-warning-for-taa-acceptance).
95+
Note that this is one reason how this "out of sync" scenario can occur, but
96+
there may be others.
97+
98+
We add an integration test that demonstrates/tests this issue [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/demo/features/taa-txn-author-acceptance.feature#L67).
99+
100+
To run the scenario either manually or using the integration tests, you can do the following:
101+
102+
- Start von-network in TAA mode:
103+
- `./manage start --taa-sample --logs`
104+
- Start the tails server as usual:
105+
- `./manage start --logs`
106+
- To run the scenario manually, start faber and let the agent know it needs to TAA-accept before doing any ledger writes:
107+
- `./run_demo faber --revocation --taa-accept`, and then you can run through all the transactions using the Swagger page.
108+
- To run the scenario via an integration test, run:
109+
- `./run_bdd -t @taa_required`

aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
8080
"connection_id": connection_id,
8181
},
8282
) # holder initiated via proposal
83+
presentation_exchange_record.presentation_request = indy_proof_request
84+
presentation_exchange_record.presentation_request_dict = (
85+
context.message.serialize()
86+
)
8387
except StorageNotFoundError: # verifier sent this request free of any proposal
8488
presentation_exchange_record = V10PresentationExchange(
8589
connection_id=connection_id,
@@ -94,7 +98,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
9498
trace=(context.message._trace is not None),
9599
)
96100

97-
presentation_exchange_record.presentation_request = indy_proof_request
98101
presentation_exchange_record = await presentation_manager.receive_request(
99102
presentation_exchange_record
100103
) # mgr only saves record: on exception, saving state null is hopeless

aries_cloudagent/protocols/present_proof/v1_0/manager.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ async def send_presentation_ack(
472472
return
473473

474474
if responder:
475-
presentation_ack_message = PresentationAck()
475+
presentation_ack_message = PresentationAck(
476+
verification_result=presentation_exchange_record.verified
477+
)
476478
presentation_ack_message._thread = {
477479
"thid": presentation_exchange_record.thread_id
478480
}
@@ -515,7 +517,7 @@ async def receive_presentation_ack(
515517
"role": V10PresentationExchange.ROLE_PROVER,
516518
},
517519
)
518-
520+
presentation_exchange_record.verified = message._verification_result
519521
presentation_exchange_record.state = (
520522
V10PresentationExchange.STATE_PRESENTATION_ACKED
521523
)

aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Represents an explicit RFC 15 ack message, adopted into present-proof protocol."""
22

3-
from marshmallow import EXCLUDE
3+
from marshmallow import EXCLUDE, fields, validate
44

55
from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema
66

@@ -21,7 +21,7 @@ class Meta:
2121
message_type = PRESENTATION_ACK
2222
schema_class = "PresentationAckSchema"
2323

24-
def __init__(self, status: str = None, **kwargs):
24+
def __init__(self, status: str = None, verification_result: str = None, **kwargs):
2525
"""
2626
Initialize an explicit ack message instance.
2727
@@ -30,6 +30,7 @@ def __init__(self, status: str = None, **kwargs):
3030
3131
"""
3232
super().__init__(status, **kwargs)
33+
self._verification_result = verification_result
3334

3435

3536
class PresentationAckSchema(V10AckSchema):
@@ -40,3 +41,10 @@ class Meta:
4041

4142
model_class = PresentationAck
4243
unknown = EXCLUDE
44+
45+
verification_result = fields.Str(
46+
required=False,
47+
description="Whether presentation is verified: true or false",
48+
example="true",
49+
validate=validate.OneOf(["true", "false"]),
50+
)

aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ async def test_send_presentation_ack_no_responder(self):
13011301
self.profile.context.injector.clear_binding(BaseResponder)
13021302
await self.manager.send_presentation_ack(exchange)
13031303

1304-
async def test_receive_presentation_ack(self):
1304+
async def test_receive_presentation_ack_a(self):
13051305
connection_record = async_mock.MagicMock(connection_id=CONN_ID)
13061306

13071307
exchange_dummy = V10PresentationExchange()
@@ -1322,6 +1322,28 @@ async def test_receive_presentation_ack(self):
13221322
V10PresentationExchange.STATE_PRESENTATION_ACKED
13231323
)
13241324

1325+
async def test_receive_presentation_ack_b(self):
1326+
connection_record = async_mock.MagicMock(connection_id=CONN_ID)
1327+
1328+
exchange_dummy = V10PresentationExchange()
1329+
message = async_mock.MagicMock(_verification_result="true")
1330+
1331+
with async_mock.patch.object(
1332+
V10PresentationExchange, "save", autospec=True
1333+
) as save_ex, async_mock.patch.object(
1334+
V10PresentationExchange, "retrieve_by_tag_filter", autospec=True
1335+
) as retrieve_ex:
1336+
retrieve_ex.return_value = exchange_dummy
1337+
exchange_out = await self.manager.receive_presentation_ack(
1338+
message, connection_record
1339+
)
1340+
save_ex.assert_called_once()
1341+
1342+
assert exchange_out.state == (
1343+
V10PresentationExchange.STATE_PRESENTATION_ACKED
1344+
)
1345+
assert exchange_out.verified == "true"
1346+
13251347
async def test_receive_problem_report(self):
13261348
connection_id = "connection-id"
13271349
stored_exchange = V10PresentationExchange(

aries_cloudagent/protocols/present_proof/v2_0/manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ async def send_pres_ack(self, pres_ex_record: V20PresExRecord):
409409
responder = self._profile.inject_or(BaseResponder)
410410

411411
if responder:
412-
pres_ack_message = V20PresAck()
412+
pres_ack_message = V20PresAck(verification_result=pres_ex_record.verified)
413413
pres_ack_message._thread = {"thid": pres_ex_record.thread_id}
414414
pres_ack_message.assign_trace_decorator(
415415
self._profile.settings, pres_ex_record.trace
@@ -445,7 +445,7 @@ async def receive_pres_ack(self, message: V20PresAck, conn_record: ConnRecord):
445445
"role": V20PresExRecord.ROLE_PROVER,
446446
},
447447
)
448-
448+
pres_ex_record.verified = message._verification_result
449449
pres_ex_record.state = V20PresExRecord.STATE_DONE
450450

451451
await pres_ex_record.save(session, reason="receive v2.0 presentation ack")

aries_cloudagent/protocols/present_proof/v2_0/messages/pres_ack.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Represents an explicit RFC 15 ack message, adopted into present-proof protocol."""
22

3-
from marshmallow import EXCLUDE
3+
from marshmallow import EXCLUDE, fields, validate
44

55
from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema
66

@@ -19,7 +19,7 @@ class Meta:
1919
message_type = PRES_20_ACK
2020
schema_class = "V20PresAckSchema"
2121

22-
def __init__(self, status: str = None, **kwargs):
22+
def __init__(self, status: str = None, verification_result: str = None, **kwargs):
2323
"""
2424
Initialize an explicit ack message instance.
2525
@@ -28,6 +28,7 @@ def __init__(self, status: str = None, **kwargs):
2828
2929
"""
3030
super().__init__(status, **kwargs)
31+
self._verification_result = verification_result
3132

3233

3334
class V20PresAckSchema(V10AckSchema):
@@ -38,3 +39,10 @@ class Meta:
3839

3940
model_class = V20PresAck
4041
unknown = EXCLUDE
42+
43+
verification_result = fields.Str(
44+
required=False,
45+
description="Whether presentation is verified: true or false",
46+
example="true",
47+
validate=validate.OneOf(["true", "false"]),
48+
)

aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2076,13 +2076,31 @@ async def test_send_pres_ack(self):
20762076
messages = responder.messages
20772077
assert len(messages) == 1
20782078

2079+
px_rec = V20PresExRecord(verified="true")
2080+
2081+
responder = MockResponder()
2082+
self.profile.context.injector.bind_instance(BaseResponder, responder)
2083+
2084+
await self.manager.send_pres_ack(px_rec)
2085+
messages = responder.messages
2086+
assert len(messages) == 1
2087+
2088+
px_rec = V20PresExRecord(verified="false")
2089+
2090+
responder = MockResponder()
2091+
self.profile.context.injector.bind_instance(BaseResponder, responder)
2092+
2093+
await self.manager.send_pres_ack(px_rec)
2094+
messages = responder.messages
2095+
assert len(messages) == 1
2096+
20792097
async def test_send_pres_ack_no_responder(self):
20802098
px_rec = V20PresExRecord()
20812099

20822100
self.profile.context.injector.clear_binding(BaseResponder)
20832101
await self.manager.send_pres_ack(px_rec)
20842102

2085-
async def test_receive_pres_ack(self):
2103+
async def test_receive_pres_ack_a(self):
20862104
conn_record = async_mock.MagicMock(connection_id=CONN_ID)
20872105

20882106
px_rec_dummy = V20PresExRecord()
@@ -2099,6 +2117,24 @@ async def test_receive_pres_ack(self):
20992117

21002118
assert px_rec_out.state == V20PresExRecord.STATE_DONE
21012119

2120+
async def test_receive_pres_ack_b(self):
2121+
conn_record = async_mock.MagicMock(connection_id=CONN_ID)
2122+
2123+
px_rec_dummy = V20PresExRecord()
2124+
message = async_mock.MagicMock(_verification_result="true")
2125+
2126+
with async_mock.patch.object(
2127+
V20PresExRecord, "save", autospec=True
2128+
) as save_ex, async_mock.patch.object(
2129+
V20PresExRecord, "retrieve_by_tag_filter", autospec=True
2130+
) as retrieve_ex:
2131+
retrieve_ex.return_value = px_rec_dummy
2132+
px_rec_out = await self.manager.receive_pres_ack(message, conn_record)
2133+
save_ex.assert_called_once()
2134+
2135+
assert px_rec_out.state == V20PresExRecord.STATE_DONE
2136+
assert px_rec_out.verified == "true"
2137+
21022138
async def test_receive_problem_report(self):
21032139
connection_id = "connection-id"
21042140
stored_exchange = V20PresExRecord(

0 commit comments

Comments
 (0)