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

🐛 Fix registering issuer did #1219

Merged
merged 39 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f6d6523
:bug: Fix registering issuer did
ff137 Dec 5, 2024
c278c8c
:art:
ff137 Dec 10, 2024
b3c33a6
:sparkles: Validate if signature request is applicable for endorsement
ff137 Dec 10, 2024
a4c916b
:white_check_mark: Test coverage for new method
ff137 Dec 10, 2024
39362b4
:white_check_mark: Full test coverage for remaining methods in endors…
ff137 Dec 10, 2024
6c6b856
:white_check_mark: Full test coverage for remaining methods in util/e…
ff137 Dec 10, 2024
10fd6a3
:white_check_mark: Full test coverage for main, while I'm here
ff137 Dec 10, 2024
db792de
:wrench: Remove `asyncio_default_fixture_loop_scope` config (again...…
ff137 Dec 10, 2024
508ecca
:art: Rearrange methods, in particular to await did transaction is en…
ff137 Dec 10, 2024
7cf8906
:white_check_mark: Fix assertion
ff137 Dec 10, 2024
95bc750
:art: Make delay an argument for the set_endorser_metadata methods
ff137 Dec 10, 2024
14420bc
:white_check_mark::zap: Speed up unit tests by overriding sleeps
ff137 Dec 10, 2024
910adb5
:construction: Test fix: `their_public_did` should be qualified
ff137 Dec 10, 2024
790f615
:art:
ff137 Dec 10, 2024
51a3c42
:bug::white_check_mark: Wait for both transactions to be endorsed bef…
ff137 Dec 10, 2024
1b9b7a4
:art: Add timeout error description
ff137 Dec 10, 2024
dd84472
Merge branch 'master' into fix-issuer-did-registration
ff137 Dec 11, 2024
46565fa
:construction: Debug error logs
ff137 Dec 11, 2024
90081f0
:art: Use bound logger
ff137 Dec 11, 2024
0bb1ab8
:white_check_mark: Update tests and reduce duration to run faster
ff137 Dec 11, 2024
70f8836
:sparkles: Replace passing of Endorsement model to just pass transact…
ff137 Dec 11, 2024
836bc95
:white_check_mark: Update tests
ff137 Dec 11, 2024
c968a40
:sparkles: Replace passing of Endorsement model to pass transaction_i…
ff137 Dec 11, 2024
2b92f97
:art:
ff137 Dec 11, 2024
9a26a3f
:wrench: Modify subscription fetch args
ff137 Dec 11, 2024
abeb830
:art: Fix heartbeat value too large
ff137 Dec 11, 2024
4ab7231
:wrench: Increase default for `WAIT_ISSUER_DID_MAX_ATTEMPTS`
ff137 Dec 11, 2024
6766553
:wrench: Increase endorser batch fetch amount
ff137 Dec 11, 2024
61547de
:art: Update log
ff137 Dec 11, 2024
09aa6ba
:art:
ff137 Dec 11, 2024
22f7280
:art: Obfuscate messages_attach from transaction record log
ff137 Dec 11, 2024
e190dce
:white_check_mark:
ff137 Dec 11, 2024
65b97a1
:wrench: increase publish-revocations timeout and make configurable (…
ff137 Dec 11, 2024
47451bf
:bug: Endorser should fetch 1 message at a time
ff137 Dec 11, 2024
4162cce
:wrench: Add `CRED_DEF_ACK_TIMEOUT` env var to configure cred def cre…
ff137 Dec 11, 2024
6567a2e
:wrench: Increase default max duration for awaiting webhooks
ff137 Dec 11, 2024
96af13b
:art: Clean up constants
ff137 Dec 11, 2024
632e299
:art: Increase default timeout
ff137 Dec 11, 2024
342347f
:art: Adjust fetch timeout
ff137 Dec 11, 2024
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
1 change: 0 additions & 1 deletion app/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,3 @@ profile = "black"
[tool.pytest.ini_options]
addopts = "--junitxml=junit.xml -p no:cacheprovider --cov-report=xml --cov-report=term"
junit_family = "xunit2"
asyncio_default_fixture_loop_scope = "function"
3 changes: 2 additions & 1 deletion app/routes/revocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from app.services import revocation_registry
from app.util.retry_method import coroutine_with_retry_until_value
from shared import PUBLISH_REVOCATIONS_TIMEOUT
from shared.log_config import get_logger

logger = get_logger(__name__)
Expand Down Expand Up @@ -209,7 +210,7 @@ async def publish_revocations(
field_name="state",
expected_value="transaction_acked",
logger=bound_logger,
max_attempts=30,
max_attempts=PUBLISH_REVOCATIONS_TIMEOUT,
retry_delay=1,
)
except asyncio.TimeoutError as e:
Expand Down
7 changes: 5 additions & 2 deletions app/services/definitions/credential_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from app.util.assert_public_did import assert_public_did
from app.util.definitions import credential_definition_from_acapy
from app.util.transaction_acked import wait_for_transaction_ack
from shared import REGISTRY_SIZE
from shared import CRED_DEF_ACK_TIMEOUT, REGISTRY_SIZE
from shared.log_config import get_logger

logger = get_logger(__name__)
Expand Down Expand Up @@ -58,7 +58,10 @@ async def create_credential_definition(

if result.txn and result.txn.transaction_id:
await wait_for_transaction_ack(
aries_controller=aries_controller, transaction_id=result.txn.transaction_id
aries_controller=aries_controller,
transaction_id=result.txn.transaction_id,
max_attempts=CRED_DEF_ACK_TIMEOUT,
retry_delay=1,
)

if support_revocation:
Expand Down
9 changes: 1 addition & 8 deletions app/services/onboarding/issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from app.services.onboarding.util.register_issuer_did import (
create_connection_with_endorser,
register_issuer_did,
wait_issuer_did_transaction_endorsed,
)
from app.util.did import qualified_did_sov
from shared.log_config import get_logger
Expand Down Expand Up @@ -115,17 +114,11 @@ async def onboard_issuer_no_public_did(
)

issuer_did = await register_issuer_did(
endorser_controller=endorser_controller,
issuer_controller=issuer_controller,
issuer_label=issuer_label,
issuer_endorser_connection_id=issuer_connection_id,
logger=bound_logger,
)

await wait_issuer_did_transaction_endorsed(
issuer_controller=issuer_controller,
issuer_connection_id=issuer_connection_id,
logger=logger,
)
except Exception as e:
bound_logger.exception("Could not create connection with endorser.")
raise CloudApiException(
Expand Down
70 changes: 50 additions & 20 deletions app/services/onboarding/util/register_issuer_did.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
import os
from logging import Logger

from aries_cloudcontroller import (
Expand All @@ -17,9 +16,7 @@
set_endorser_info,
set_endorser_role,
)
from shared import ACAPY_ENDORSER_ALIAS

MAX_ATTEMPTS = int(os.getenv("WAIT_ISSUER_DID_MAX_ATTEMPTS", "15"))
from shared import ACAPY_ENDORSER_ALIAS, ISSUER_DID_ENDORSE_TIMEOUT


async def create_connection_with_endorser(
Expand Down Expand Up @@ -162,32 +159,46 @@ async def configure_endorsement(

async def register_issuer_did(
*,
endorser_controller: AcaPyClient,
issuer_controller: AcaPyClient,
issuer_label: str,
issuer_endorser_connection_id: str,
logger: Logger,
) -> DID:
logger.debug("Accepting TAA on behalf of issuer")
await acapy_ledger.accept_taa_if_required(issuer_controller)

logger.info("Creating DID for issuer")
issuer_did = await acapy_wallet.create_did(issuer_controller)

await acapy_ledger.register_nym_on_ledger(
endorser_controller,
issuer_controller,
did=issuer_did.did,
verkey=issuer_did.verkey,
alias=issuer_label,
create_transaction_for_endorser=True,
)

logger.debug("Waiting for issuer DID transaction to be endorsed")
await wait_transactions_endorsed( # Needs to be endorsed before setting public DID
issuer_controller=issuer_controller,
issuer_connection_id=issuer_endorser_connection_id,
logger=logger,
)

logger.debug("Accepting TAA on behalf of issuer")
await acapy_ledger.accept_taa_if_required(issuer_controller)
# NOTE: Still needs endorsement in 0.7.5 release
# Otherwise did has no associated services.
logger.debug("Setting public DID for issuer")
await acapy_wallet.set_public_did(
issuer_controller,
did=issuer_did.did,
create_transaction_for_endorser=True,
)

logger.debug("Waiting for ATTRIB transaction to be endorsed")
await wait_transactions_endorsed( # Needs to be endorsed before continuing
issuer_controller=issuer_controller,
issuer_connection_id=issuer_endorser_connection_id,
logger=logger,
)

logger.debug("Issuer DID registered.")
return issuer_did

Expand Down Expand Up @@ -239,12 +250,12 @@ async def wait_endorser_connection_completed(
raise asyncio.TimeoutError


async def wait_issuer_did_transaction_endorsed(
async def wait_transactions_endorsed(
*,
issuer_controller: AcaPyClient,
issuer_connection_id: str,
logger: Logger,
max_attempts: int = MAX_ATTEMPTS,
max_attempts: int = ISSUER_DID_ENDORSE_TIMEOUT,
retry_delay: float = 1.0,
) -> None:
attempt = 0
Expand All @@ -255,19 +266,38 @@ async def wait_issuer_did_transaction_endorsed(
await issuer_controller.endorse_transaction.get_records()
)

for transaction in transactions_response.results:
if (
transaction.connection_id == issuer_connection_id
and transaction.state == "transaction_acked"
):
return
transactions = [
transaction
for transaction in transactions_response.results
if transaction.connection_id == issuer_connection_id
]

if not transactions:
logger.error(
"No transactions found for connection {}. Found {} transactions.",
issuer_connection_id,
transactions_response,
)
raise CloudApiException("No transactions found for connection", 404)

all_acked = all(
transaction.state == "transaction_acked" for transaction in transactions
)

if all_acked:
return
else:
logger.debug(
"Waiting for transaction acknowledgements. Current states: %s",
", ".join(f"{t.transaction_id}: {t.state}" for t in transactions),
)

except Exception as e: # pylint: disable=W0718
if attempt + 1 == max_attempts:
logger.error(
"Maximum number of retries exceeded with exception. Failing."
)
raise asyncio.TimeoutError from e # Raise TimeoutError if max attempts exceeded
raise asyncio.TimeoutError("Timeout waiting for endorsement") from e

logger.warning(
(
Expand All @@ -284,4 +314,4 @@ async def wait_issuer_did_transaction_endorsed(
attempt += 1

logger.error("Maximum number of retries exceeded while waiting for transaction ack")
raise asyncio.TimeoutError
raise asyncio.TimeoutError("Timeout waiting for endorsement")
19 changes: 14 additions & 5 deletions app/services/onboarding/util/set_endorser_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@

# todo: Migrate to endorser service
async def set_endorser_role(
*, endorser_controller: AcaPyClient, endorser_connection_id: str, logger: Logger
*,
endorser_controller: AcaPyClient,
endorser_connection_id: str,
logger: Logger,
delay: float = DEFAULT_DELAY,
):
try:
logger.debug("Setting roles for endorser on endorser-issuer connection.")
Expand All @@ -24,7 +28,7 @@ async def set_endorser_role(
transaction_my_job="TRANSACTION_ENDORSER",
)
logger.debug("Successfully set endorser role.")
await asyncio.sleep(DEFAULT_DELAY) # Allow ACA-Py records to update
await asyncio.sleep(delay) # Allow ACA-Py records to update
except CloudApiException as e:
logger.error("Failed to set endorser role: {}.", e)
raise CloudApiException(
Expand All @@ -35,7 +39,11 @@ async def set_endorser_role(


async def set_author_role(
*, issuer_controller: AcaPyClient, issuer_connection_id: str, logger: Logger
*,
issuer_controller: AcaPyClient,
issuer_connection_id: str,
logger: Logger,
delay: float = DEFAULT_DELAY,
):
try:
logger.debug("Setting roles for author on issuer-endorser connection")
Expand All @@ -46,7 +54,7 @@ async def set_author_role(
transaction_my_job="TRANSACTION_AUTHOR",
)
logger.debug("Successfully set author role.")
await asyncio.sleep(DEFAULT_DELAY) # Allow ACA-Py records to update
await asyncio.sleep(delay) # Allow ACA-Py records to update
except CloudApiException as e:
logger.error("Failed to set author role: {}.", e)
raise CloudApiException(
Expand All @@ -62,6 +70,7 @@ async def set_endorser_info(
issuer_connection_id: str,
endorser_did: str,
logger: Logger,
delay: float = DEFAULT_DELAY,
):
try:
logger.debug("Setting endorser info on issuer-endorser connection")
Expand All @@ -72,7 +81,7 @@ async def set_endorser_info(
endorser_did=endorser_did,
)
logger.debug("Successfully set endorser info.")
await asyncio.sleep(DEFAULT_DELAY) # Allow ACA-Py records to update
await asyncio.sleep(delay) # Allow ACA-Py records to update
except CloudApiException as e:
logger.error("Failed to set endorser info: {}.", e)
raise CloudApiException(
Expand Down
5 changes: 3 additions & 2 deletions app/tests/e2e/verifier/test_proof_revoked_credential.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import time
from typing import List

Expand Down Expand Up @@ -34,7 +35,7 @@ async def test_proof_revoked_credential(
alice_member_client: RichAsyncClient,
acme_and_alice_connection: AcmeAliceConnect,
):
time.sleep(10) # moment for revocation registry to update
await asyncio.sleep(14) # moment for revocation registry to update
# todo: remove sleep when issue resolved: https://github.com/openwallet-foundation/acapy/issues/3018

# Do proof request
Expand Down Expand Up @@ -124,7 +125,7 @@ async def test_regression_proof_revoked_credential(
alice_member_client: RichAsyncClient,
acme_and_alice_connection: AcmeAliceConnect,
):
time.sleep(10) # moment for revocation registry to update
await asyncio.sleep(14) # moment for revocation registry to update
# todo: remove sleep when issue resolved: https://github.com/openwallet-foundation/acapy/issues/3018

referent = get_or_issue_regression_cred_revoked.referent
Expand Down
36 changes: 18 additions & 18 deletions app/tests/services/onboarding/test_onboarding.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import AsyncMock, patch

import pytest
from aries_cloudcontroller import (
DID,
Expand Down Expand Up @@ -88,22 +90,19 @@ async def test_onboard_issuer_no_public_did(

endorser_controller = get_mock_agent_controller()

# Mock the necessary functions and methods
when(acapy_wallet).get_public_did(controller=mock_agent_controller).thenRaise(
CloudApiException(detail="Error")
)
when(acapy_wallet).get_public_did(controller=endorser_controller).thenReturn(
to_async(did_object)
)

when(endorser_controller.out_of_band).create_invitation(...).thenReturn(
to_async(InvitationRecord(invitation=InvitationMessage()))
)

# Mock responses
when(mock_agent_controller.out_of_band).receive_invitation(...).thenReturn(
to_async(ConnRecord(connection_id=issuer_connection_id))
)

when(endorser_controller.connection).get_connections(...).thenReturn(
to_async(
ConnectionList(
Expand All @@ -115,20 +114,17 @@ async def test_onboard_issuer_no_public_did(
)
)
)

when(mock_agent_controller.endorse_transaction).set_endorser_role(...).thenReturn(
to_async()
)

when(endorser_controller.endorse_transaction).set_endorser_role(...).thenReturn(
to_async()
)
when(mock_agent_controller.endorse_transaction).set_endorser_info(...).thenAnswer(
lambda conn_id, endorser_did: to_async()
)

when(mock_agent_controller.endorse_transaction).get_records(...).thenReturn(
to_async(
when(mock_agent_controller.endorse_transaction).get_records(...).thenAnswer(
lambda: to_async( # lambda to avoid "cannot reuse already awaited coroutine"
TransactionList(
results=[
TransactionRecord(
Expand All @@ -138,7 +134,6 @@ async def test_onboard_issuer_no_public_did(
)
)
)

when(acapy_wallet).create_did(mock_agent_controller).thenReturn(
to_async(did_object)
)
Expand All @@ -156,27 +151,32 @@ async def test_onboard_issuer_no_public_did(
)
)

onboard_result = await issuer.onboard_issuer(
issuer_label="issuer_name",
endorser_controller=endorser_controller,
issuer_controller=mock_agent_controller,
issuer_wallet_id="issuer_wallet_id",
)
# Patch asyncio.sleep to return immediately
with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep:
onboard_result = await issuer.onboard_issuer(
issuer_label="issuer_name",
endorser_controller=endorser_controller,
issuer_controller=mock_agent_controller,
issuer_wallet_id="issuer_wallet_id",
)

# Assertions
assert_that(onboard_result).has_did("did:sov:WgWxqztrNooG92RXvxSTWv")
verify(acapy_ledger).accept_taa_if_required(mock_agent_controller)
verify(acapy_wallet).create_did(mock_agent_controller)
verify(acapy_ledger).register_nym_on_ledger(
endorser_controller,
mock_agent_controller,
did="WgWxqztrNooG92RXvxSTWv",
verkey="WgWxqztrNooG92RXvxSTWvWgWxqztrNooG92RXvxSTWv",
alias="issuer_name",
create_transaction_for_endorser=True,
)
verify(acapy_ledger).accept_taa_if_required(mock_agent_controller)
verify(acapy_wallet).set_public_did(
mock_agent_controller,
did="WgWxqztrNooG92RXvxSTWv",
create_transaction_for_endorser=True,
)
mock_sleep.assert_awaited() # Ensure that sleep was called and patched


@pytest.mark.anyio
Expand Down
Loading
Loading