diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index eb487c5f57..f36fcbf963 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "dockerfile": "Dockerfile", "context": "..", "args": { - "VARIANT": "3.12-bullseye", + "VARIANT": "3.12-bookworm", "POETRY_VERSION": "1.7.1" } }, @@ -48,16 +48,16 @@ "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {} }, - + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", - + "remoteEnv": { "RUST_LOG":"aries-askar::log::target=error" //"PATH": "${containerEnv:PATH}:${workspaceRoot}/.venv/bin" }, - + "mounts": [], "postCreateCommand": "bash ./.devcontainer/post-install.sh" - -} \ No newline at end of file + +} diff --git a/.github/workflows/sonar-pr.yml b/.github/workflows/sonar-pr.yml index 22d06d4ba4..d1052aaf53 100644 --- a/.github/workflows/sonar-pr.yml +++ b/.github/workflows/sonar-pr.yml @@ -15,7 +15,7 @@ jobs: with: fetch-depth: 0 - name: Download PR number artifact - uses: dawidd6/action-download-artifact@v7 + uses: dawidd6/action-download-artifact@v8 with: workflow: Tests run_id: ${{ github.event.workflow_run.id }} @@ -26,7 +26,7 @@ jobs: with: path: ./PR_NUMBER - name: Download Test Coverage - uses: dawidd6/action-download-artifact@v7 + uses: dawidd6/action-download-artifact@v8 with: workflow: Tests run_id: ${{ github.event.workflow_run.id }} diff --git a/acapy_agent/anoncreds/base.py b/acapy_agent/anoncreds/base.py index 4c5b79d345..9163d8d431 100644 --- a/acapy_agent/anoncreds/base.py +++ b/acapy_agent/anoncreds/base.py @@ -127,7 +127,11 @@ async def get_revocation_registry_definition( @abstractmethod async def get_revocation_list( - self, profile: Profile, revocation_registry_id: str, timestamp: int + self, + profile: Profile, + revocation_registry_id: str, + timestamp_from: Optional[int] = 0, + timestamp_to: Optional[int] = None, ) -> GetRevListResult: """Get a revocation list from the registry.""" diff --git a/acapy_agent/anoncreds/default/did_web/registry.py b/acapy_agent/anoncreds/default/did_web/registry.py index 718b624842..c53ed2c4cd 100644 --- a/acapy_agent/anoncreds/default/did_web/registry.py +++ b/acapy_agent/anoncreds/default/did_web/registry.py @@ -88,7 +88,11 @@ async def register_revocation_registry_definition( raise NotImplementedError() async def get_revocation_list( - self, profile: Profile, revocation_registry_id: str, timestamp: int + self, + profile: Profile, + revocation_registry_id: str, + timestamp_from: Optional[int] = 0, + timestamp_to: Optional[int] = None, ) -> GetRevListResult: """Get a revocation list from the registry.""" raise NotImplementedError() diff --git a/acapy_agent/anoncreds/holder.py b/acapy_agent/anoncreds/holder.py index 08006fe176..11f47fea78 100644 --- a/acapy_agent/anoncreds/holder.py +++ b/acapy_agent/anoncreds/holder.py @@ -372,12 +372,12 @@ async def store_credential_w3c( return credential_id - async def get_credentials(self, start: int, count: int, wql: dict): + async def get_credentials(self, *, offset: int, limit: int, wql: dict): """Get credentials stored in the wallet. Args: - start: Starting index - count: Number of records to return + offset: Starting index + limit: Number of records to return wql: wql query dict """ @@ -388,8 +388,8 @@ async def get_credentials(self, start: int, count: int, wql: dict): rows = self.profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=wql, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self.profile.settings.get("wallet.askar_profile"), ) async for row in rows: @@ -406,8 +406,9 @@ async def get_credentials_for_presentation_request_by_referent( self, presentation_request: dict, referents: Sequence[str], - start: int, - count: int, + *, + offset: int, + limit: int, extra_query: Optional[dict] = None, ): """Get credentials stored in the wallet. @@ -415,8 +416,8 @@ async def get_credentials_for_presentation_request_by_referent( Args: presentation_request: Valid presentation request from issuer referents: Presentation request referents to use to search for creds - start: Starting index - count: Maximum number of records to return + offset: Starting index + limit: Maximum number of records to return extra_query: wql query dict """ @@ -459,8 +460,8 @@ async def get_credentials_for_presentation_request_by_referent( rows = self.profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=tag_filter, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self.profile.settings.get("wallet.askar_profile"), ) async for row in rows: diff --git a/acapy_agent/anoncreds/models/credential_proposal.py b/acapy_agent/anoncreds/models/credential_proposal.py index 3d41d08747..ba7365b292 100644 --- a/acapy_agent/anoncreds/models/credential_proposal.py +++ b/acapy_agent/anoncreds/models/credential_proposal.py @@ -2,7 +2,7 @@ import re -from marshmallow import fields +from marshmallow import fields, validate from ...core.profile import Profile from ...messaging.models.openapi import OpenAPISchema @@ -13,12 +13,30 @@ ANONCREDS_DID_VALIDATE, ANONCREDS_SCHEMA_ID_EXAMPLE, ANONCREDS_SCHEMA_ID_VALIDATE, + INDY_DID_VALIDATE, + MAJOR_MINOR_VERSION_EXAMPLE, + MAJOR_MINOR_VERSION_VALIDATE, ) class AnoncredsCredentialDefinitionProposal(OpenAPISchema): """Query string parameters for credential definition searches.""" + cred_def_id = fields.Str( + required=False, + validate=ANONCREDS_CRED_DEF_ID_VALIDATE, + metadata={ + "description": "Credential definition id. This is the only required field.", + "example": ANONCREDS_CRED_DEF_ID_EXAMPLE, + }, + ) + issuer_id = fields.Str( + required=False, + # TODO: INDY_DID_VALIDATE should be removed when indy sov did's + # are represented by did:sov:{nym} in acapy + validate=validate.NoneOf([ANONCREDS_DID_VALIDATE, INDY_DID_VALIDATE]), + metadata={"description": "Issuer DID", "example": ANONCREDS_DID_EXAMPLE}, + ) schema_id = fields.Str( required=False, validate=ANONCREDS_SCHEMA_ID_VALIDATE, @@ -27,17 +45,25 @@ class AnoncredsCredentialDefinitionProposal(OpenAPISchema): "example": ANONCREDS_SCHEMA_ID_EXAMPLE, }, ) - issuer_id = fields.Str( + schema_issuer_id = fields.Str( required=False, - validate=ANONCREDS_DID_VALIDATE, - metadata={"description": "Issuer DID", "example": ANONCREDS_DID_EXAMPLE}, + # TODO: INDY_DID_VALIDATE should be removed when indy sov did's + # are represented by did:sov:{nym} in acapy + validate=validate.NoneOf([ANONCREDS_DID_VALIDATE, INDY_DID_VALIDATE]), + metadata={ + "description": "Schema identifier", + "example": ANONCREDS_SCHEMA_ID_EXAMPLE, + }, ) - cred_def_id = fields.Str( + schema_name = fields.Str( + required=False, metadata={"description": "Schema name", "example": "simple"} + ) + schema_version = fields.Str( required=False, - validate=ANONCREDS_CRED_DEF_ID_VALIDATE, + validate=MAJOR_MINOR_VERSION_VALIDATE, metadata={ - "description": "Credential definition id", - "example": ANONCREDS_CRED_DEF_ID_EXAMPLE, + "description": "Schema version", + "example": MAJOR_MINOR_VERSION_EXAMPLE, }, ) diff --git a/acapy_agent/anoncreds/models/utils.py b/acapy_agent/anoncreds/models/utils.py index 51f140844f..ac5797190c 100644 --- a/acapy_agent/anoncreds/models/utils.py +++ b/acapy_agent/anoncreds/models/utils.py @@ -39,8 +39,8 @@ async def get_requested_creds_from_proof_request_preview( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=proof_request, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError(_get_value_error_msg(proof_request, referent)) @@ -61,8 +61,8 @@ async def get_requested_creds_from_proof_request_preview( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=proof_request, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError(_get_value_error_msg(proof_request, referent)) diff --git a/acapy_agent/anoncreds/routes.py b/acapy_agent/anoncreds/routes.py index 2f877c5cc6..a889fa27a4 100644 --- a/acapy_agent/anoncreds/routes.py +++ b/acapy_agent/anoncreds/routes.py @@ -21,11 +21,11 @@ ANONCREDS_CRED_DEF_ID_EXAMPLE, ANONCREDS_DID_EXAMPLE, ANONCREDS_REV_REG_ID_EXAMPLE, + ANONCREDS_REV_REG_ID_VALIDATE, ANONCREDS_SCHEMA_ID_EXAMPLE, UUIDFour, ) from ..revocation.error import RevocationNotSupportedError -from ..revocation.routes import RevocationModuleResponseSchema, RevRegIdMatchInfoSchema from ..storage.error import StorageNotFoundError from ..utils.profiles import is_not_anoncreds_profile_raise_web_exception from .base import ( @@ -62,6 +62,23 @@ ) +class AnoncredsRevocationModuleResponseSchema(OpenAPISchema): + """Response schema for Revocation Module.""" + + +class AnonCredsRevRegIdMatchInfoSchema(OpenAPISchema): + """Path parameters and validators for request taking rev reg id.""" + + rev_reg_id = fields.Str( + required=True, + validate=ANONCREDS_REV_REG_ID_VALIDATE, + metadata={ + "description": "Revocation Registry identifier", + "example": ANONCREDS_REV_REG_ID_EXAMPLE, + }, + ) + + class SchemaIdMatchInfo(OpenAPISchema): """Path parameters and validators for request taking schema id.""" @@ -699,8 +716,8 @@ async def rev_list_post(request: web.BaseRequest): tags=["anoncreds - revocation"], summary="Upload local tails file to server", ) -@match_info_schema(RevRegIdMatchInfoSchema()) -@response_schema(RevocationModuleResponseSchema(), description="") +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) +@response_schema(AnoncredsRevocationModuleResponseSchema(), description="") @tenant_authentication async def upload_tails_file(request: web.BaseRequest): """Request handler to upload local tails file for revocation registry. @@ -735,8 +752,8 @@ async def upload_tails_file(request: web.BaseRequest): tags=["anoncreds - revocation"], summary="Update the active registry", ) -@match_info_schema(RevRegIdMatchInfoSchema()) -@response_schema(RevocationModuleResponseSchema(), description="") +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) +@response_schema(AnoncredsRevocationModuleResponseSchema(), description="") @tenant_authentication async def set_active_registry(request: web.BaseRequest): """Request handler to set the active registry. diff --git a/acapy_agent/anoncreds/tests/test_holder.py b/acapy_agent/anoncreds/tests/test_holder.py index 9667fe76e5..c442fd76bb 100644 --- a/acapy_agent/anoncreds/tests/test_holder.py +++ b/acapy_agent/anoncreds/tests/test_holder.py @@ -373,7 +373,7 @@ async def test_store_credential_failed_trx(self, *_): async def test_get_credentials(self, _): async with self.profile.session() as session: await session.handle.insert(CATEGORY_CREDENTIAL, json.dumps(MOCK_CRED)) - result = await self.holder.get_credentials(0, 10, {}) + result = await self.holder.get_credentials(offset=0, limit=10, wql={}) assert isinstance(result, list) assert len(result) == 1 @@ -386,9 +386,9 @@ async def test_get_credentials_errors(self): ) with self.assertRaises(AnonCredsHolderError): - await self.holder.get_credentials(0, 10, {}) + await self.holder.get_credentials(offset=0, limit=10, wql={}) with self.assertRaises(AnonCredsHolderError): - await self.holder.get_credentials(0, 10, {}) + await self.holder.get_credentials(offset=0, limit=10, wql={}) async def test_get_credentials_for_presentation_request_by_referent(self): self.profile.store.scan = mock.Mock( @@ -408,13 +408,13 @@ async def test_get_credentials_for_presentation_request_by_referent(self): "restrictions": [{"schema_name": "MYCO Biomarker"}], } await self.holder.get_credentials_for_presentation_request_by_referent( - mock_pres_req, None, start=0, count=10 + mock_pres_req, None, offset=0, limit=10 ) # non-existent referent with self.assertRaises(AnonCredsHolderError): await self.holder.get_credentials_for_presentation_request_by_referent( - mock_pres_req, "not-found-ref", start=0, count=10 + mock_pres_req, "not-found-ref", offset=0, limit=10 ) @mock.patch.object(Credential, "load", return_value=MockCredential()) diff --git a/acapy_agent/askar/profile.py b/acapy_agent/askar/profile.py index 2b5c8bfe62..e562ff88ff 100644 --- a/acapy_agent/askar/profile.py +++ b/acapy_agent/askar/profile.py @@ -42,7 +42,9 @@ def __init__( profile_id: Optional[str] = None, ): """Create a new AskarProfile instance.""" - super().__init__(context=context, name=opened.name, created=opened.created) + super().__init__( + context=context, name=profile_id or opened.name, created=opened.created + ) self.opened = opened self.ledger_pool: Optional[IndyVdrLedgerPool] = None self.profile_id = profile_id @@ -52,7 +54,7 @@ def __init__( @property def name(self) -> str: """Accessor for the profile name.""" - return self.opened.name + return self.profile_id or self.opened.name @property def store(self) -> Store: diff --git a/acapy_agent/askar/profile_anon.py b/acapy_agent/askar/profile_anon.py index 9a3cb3e622..90c6e7a09d 100644 --- a/acapy_agent/askar/profile_anon.py +++ b/acapy_agent/askar/profile_anon.py @@ -44,7 +44,9 @@ def __init__( profile_id: Optional[str] = None, ): """Create a new AskarProfile instance.""" - super().__init__(context=context, name=opened.name, created=opened.created) + super().__init__( + context=context, name=profile_id or opened.name, created=opened.created + ) self.opened = opened self.ledger_pool: Optional[IndyVdrLedgerPool] = None self.profile_id = profile_id @@ -54,7 +56,7 @@ def __init__( @property def name(self) -> str: """Accessor for the profile name.""" - return self.opened.name + return self.profile_id or self.opened.name @property def store(self) -> Store: diff --git a/acapy_agent/connections/routes.py b/acapy_agent/connections/routes.py index a0edc407ea..964538f142 100644 --- a/acapy_agent/connections/routes.py +++ b/acapy_agent/connections/routes.py @@ -16,7 +16,10 @@ from ..connections.models.conn_record import ConnRecord, ConnRecordSchema from ..messaging.models.base import BaseModelError from ..messaging.models.openapi import OpenAPISchema -from ..messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ..messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ..messaging.valid import ( ENDPOINT_EXAMPLE, ENDPOINT_VALIDATE, @@ -247,20 +250,6 @@ class EndpointsResultSchema(OpenAPISchema): ) -def connection_sort_key(conn): - """Get the sorting key for a particular connection.""" - - conn_rec_state = ConnRecord.State.get(conn["state"]) - if conn_rec_state is ConnRecord.State.ABANDONED: - pfx = "2" - elif conn_rec_state is ConnRecord.State.INVITATION: - pfx = "1" - else: - pfx = "0" - - return pfx + conn["created_at"] - - @docs( tags=["connection"], summary="Query agent-to-agent connections", @@ -305,7 +294,7 @@ async def connections_list(request: web.BaseRequest): if request.query.get("connection_protocol"): post_filter["connection_protocol"] = request.query["connection_protocol"] - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) profile = context.profile try: @@ -315,11 +304,12 @@ async def connections_list(request: web.BaseRequest): tag_filter, limit=limit, offset=offset, + order_by=order_by, + descending=descending, post_filter_positive=post_filter, alt=True, ) results = [record.serialize() for record in records] - results.sort(key=connection_sort_key) except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err diff --git a/acapy_agent/connections/tests/test_routes.py b/acapy_agent/connections/tests/test_routes.py index 1a4625e1a5..5613924f61 100644 --- a/acapy_agent/connections/tests/test_routes.py +++ b/acapy_agent/connections/tests/test_routes.py @@ -86,7 +86,7 @@ async def test_connections_list(self): ) ), ] - mock_conn_rec.query.return_value = [conns[2], conns[0], conns[1]] # jumbled + mock_conn_rec.query.return_value = conns with mock.patch.object(test_module.web, "json_response") as mock_response: await test_module.connections_list(self.request) @@ -100,6 +100,8 @@ async def test_connections_list(self): }, limit=100, offset=0, + order_by="id", + descending=False, post_filter_positive={ "their_role": list(ConnRecord.Role.REQUESTER.value), "connection_protocol": "connections/1.0", diff --git a/acapy_agent/holder/routes.py b/acapy_agent/holder/routes.py index 37c1d5e1e7..1fad1fa940 100644 --- a/acapy_agent/holder/routes.py +++ b/acapy_agent/holder/routes.py @@ -13,6 +13,7 @@ ) from aries_askar import AskarErrorCode from marshmallow import fields +from marshmallow.validate import Range from ..admin.decorators.auth import tenant_authentication from ..admin.request_context import AdminRequestContext @@ -33,6 +34,7 @@ NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, ) +from ..storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE from ..storage.error import StorageError, StorageNotFoundError from ..storage.vc_holder.base import VCHolder from ..storage.vc_holder.vc_record import VCRecordSchema @@ -66,17 +68,34 @@ class CredentialsListQueryStringSchema(OpenAPISchema): start = fields.Str( required=False, + load_default=0, validate=NUM_STR_WHOLE_VALIDATE, - metadata={"description": "Start index", "example": NUM_STR_WHOLE_EXAMPLE}, + metadata={ + "description": "Start index (DEPRECATED - use offset instead)", + "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, + }, ) count = fields.Str( required=False, + load_default=10, validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=Range(min=1, max=MAXIMUM_PAGE_SIZE), + metadata={"description": "Number of results to return", "example": 50}, + ) + offset = fields.Int( + required=False, + validate=Range(min=0), + metadata={"description": "Offset for pagination", "example": 0}, + ) wql = fields.Str( required=False, validate=INDY_WQL_VALIDATE, @@ -379,25 +398,32 @@ async def credentials_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - start = request.query.get("start") - count = request.query.get("count") + + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json wql encoded_wql = request.query.get("wql") or "{}" wql = json.loads(encoded_wql) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - if context.settings.get(wallet_type_config) == "askar-anoncreds": holder = AnonCredsHolder(context.profile) - credentials = await holder.get_credentials(start, count, wql) + credentials = await holder.get_credentials(limit=limit, offset=offset, wql=wql) else: async with context.profile.session() as session: holder = session.inject(IndyHolder) try: - credentials = await holder.get_credentials(start, count, wql) + credentials = await holder.get_credentials( + limit=limit, offset=offset, wql=wql + ) except IndyHolderError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err @@ -476,7 +502,6 @@ async def w3c_cred_remove(request: web.BaseRequest): summary="Fetch W3C credentials from wallet", ) @request_schema(W3CCredentialsListRequestSchema()) -@querystring_schema(CredentialsListQueryStringSchema()) @response_schema(VCRecordListSchema(), 200, description="") @tenant_authentication async def w3c_creds_list(request: web.BaseRequest): diff --git a/acapy_agent/indy/credx/holder.py b/acapy_agent/indy/credx/holder.py index 27ba7837e2..089f1620d1 100644 --- a/acapy_agent/indy/credx/holder.py +++ b/acapy_agent/indy/credx/holder.py @@ -236,12 +236,12 @@ async def store_credential( return credential_id - async def get_credentials(self, start: int, count: int, wql: dict): + async def get_credentials(self, *, offset: int, limit: int, wql: dict): """Get credentials stored in the wallet. Args: - start: Starting index - count: Number of records to return + offset: Starting index + limit: Number of records to return wql: wql query dict """ @@ -252,8 +252,8 @@ async def get_credentials(self, start: int, count: int, wql: dict): rows = self._profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=wql, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self._profile.settings.get("wallet.askar_profile"), ) async for row in rows: @@ -270,8 +270,9 @@ async def get_credentials_for_presentation_request_by_referent( self, presentation_request: dict, referents: Sequence[str], - start: int, - count: int, + *, + offset: int, + limit: int, extra_query: Optional[dict] = None, ): """Get credentials stored in the wallet. @@ -279,8 +280,8 @@ async def get_credentials_for_presentation_request_by_referent( Args: presentation_request: Valid presentation request from issuer referents: Presentation request referents to use to search for creds - start: Starting index - count: Maximum number of records to return + offset: Starting index + limit: Maximum number of records to return extra_query: wql query dict """ @@ -323,8 +324,8 @@ async def get_credentials_for_presentation_request_by_referent( rows = self._profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=tag_filter, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self._profile.settings.get("wallet.askar_profile"), ) async for row in rows: diff --git a/acapy_agent/indy/credx/tests/test_cred_issuance.py b/acapy_agent/indy/credx/tests/test_cred_issuance.py index 9b8c222dc2..f0154b0a7b 100644 --- a/acapy_agent/indy/credx/tests/test_cred_issuance.py +++ b/acapy_agent/indy/credx/tests/test_cred_issuance.py @@ -132,7 +132,7 @@ async def test_issue_store_non_rev(self): assert not await self.holder.get_mime_type(cred_id, "name") - creds = await self.holder.get_credentials(None, None, None) + creds = await self.holder.get_credentials(offset=None, limit=None, wql=None) assert len(creds) == 1 assert creds[0] == stored_cred @@ -142,9 +142,9 @@ async def test_issue_store_non_rev(self): await self.holder.get_credentials_for_presentation_request_by_referent( PRES_REQ_NON_REV, None, - 0, - 10, - {}, + offset=0, + limit=10, + extra_query={}, ) ) assert pres_creds == [ @@ -247,7 +247,7 @@ async def test_issue_store_rev(self): assert found stored_cred = json.loads(found) - creds = await self.holder.get_credentials(None, None, None) + creds = await self.holder.get_credentials(offset=None, limit=None, wql=None) assert len(creds) == 1 assert creds[0] == stored_cred @@ -257,9 +257,9 @@ async def test_issue_store_rev(self): await self.holder.get_credentials_for_presentation_request_by_referent( PRES_REQ_REV, None, - 0, - 10, - {}, + offset=0, + limit=10, + extra_query={}, ) ) assert pres_creds == [ diff --git a/acapy_agent/indy/models/xform.py b/acapy_agent/indy/models/xform.py index 6e80794889..c8082ce576 100644 --- a/acapy_agent/indy/models/xform.py +++ b/acapy_agent/indy/models/xform.py @@ -34,8 +34,8 @@ async def indy_proof_req_preview2indy_requested_creds( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=indy_proof_req, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError( @@ -87,8 +87,8 @@ async def indy_proof_req_preview2indy_requested_creds( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=indy_proof_req, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError( diff --git a/acapy_agent/messaging/models/base_record.py b/acapy_agent/messaging/models/base_record.py index e613c6e6d9..f32a8e0c4e 100644 --- a/acapy_agent/messaging/models/base_record.py +++ b/acapy_agent/messaging/models/base_record.py @@ -293,6 +293,8 @@ async def query( *, limit: Optional[int] = None, offset: Optional[int] = None, + order_by: Optional[str] = None, + descending: bool = False, post_filter_positive: Optional[dict] = None, post_filter_negative: Optional[dict] = None, alt: bool = False, @@ -304,6 +306,8 @@ async def query( tag_filter: An optional dictionary of tag filter clauses limit: The maximum number of records to retrieve offset: The offset to start retrieving records from + order_by: An optional field by which to order the records. + descending: Whether to order the records in descending order. post_filter_positive: Additional value filters to apply matching positively post_filter_negative: Additional value filters to apply matching negatively alt: set to match any (positive=True) value or miss all (positive=False) @@ -327,11 +331,15 @@ async def query( tag_query=tag_query, limit=limit, offset=offset, + order_by=order_by, + descending=descending, ) else: rows = await storage.find_all_records( type_filter=cls.RECORD_TYPE, tag_query=tag_query, + order_by=order_by, + descending=descending, ) num_results_post_filter = 0 # used if applying pagination post-filter diff --git a/acapy_agent/messaging/models/paginated_query.py b/acapy_agent/messaging/models/paginated_query.py index a929baedc8..d52e8ae952 100644 --- a/acapy_agent/messaging/models/paginated_query.py +++ b/acapy_agent/messaging/models/paginated_query.py @@ -4,7 +4,7 @@ from aiohttp.web import BaseRequest from marshmallow import fields -from marshmallow.validate import Range +from marshmallow.validate import OneOf, Range from ...messaging.models.openapi import OpenAPISchema from ...storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE @@ -25,18 +25,47 @@ class PaginatedQuerySchema(OpenAPISchema): validate=Range(min=0), metadata={"description": "Offset for pagination", "example": 0}, ) + order_by = fields.Str( + required=False, + load_default="id", + validate=OneOf(["id"]), # only one possible column supported in askar + metadata={ + "description": ( + 'The column to order results by. Only "id" is currently supported.' + ) + }, + error_messages={"validator_failed": '`order_by` only supports column "id"'}, + ) + descending = fields.Bool( + required=False, + load_default=False, + truthy={"true", "1", "yes"}, + falsy={"false", "0", "no"}, + metadata={"description": "Order results in descending order if true"}, + error_messages={"invalid": "Not a valid boolean."}, + ) -def get_limit_offset(request: BaseRequest) -> Tuple[int, int]: - """Read the limit and offset query parameters from a request as ints, with defaults. +def get_paginated_query_params(request: BaseRequest) -> Tuple[int, int, str, bool]: + """Read the limit, offset, order_by, and descending query parameters from a request. Args: - request: aiohttp request object + request: aiohttp request object. Returns: - A tuple of the limit and offset values + A tuple containing: + - limit (int): The number of results to return, defaulting to DEFAULT_PAGE_SIZE. + - offset (int): The offset for pagination, defaulting to 0. + - order_by (str): The field by which to order results, defaulting to "id". + - descending (bool): Order results in descending order; defaults to False. """ limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) offset = int(request.query.get("offset", 0)) - return limit, offset + order_by = request.query.get("order_by", "id") + + # Convert the 'descending' parameter to a boolean + descending_str = request.query.get("descending", "False").lower() + descending = descending_str in {"true", "1", "yes"} + + return limit, offset, order_by, descending diff --git a/acapy_agent/messaging/models/tests/test_base_record.py b/acapy_agent/messaging/models/tests/test_base_record.py index ab262ff3ee..0c18b80884 100644 --- a/acapy_agent/messaging/models/tests/test_base_record.py +++ b/acapy_agent/messaging/models/tests/test_base_record.py @@ -164,6 +164,8 @@ async def test_query(self): result = await BaseRecordImpl.query(session, tag_filter) mock_storage.find_all_records.assert_awaited_once_with( type_filter=BaseRecordImpl.RECORD_TYPE, + order_by=None, + descending=False, tag_query=tag_filter, ) assert result and isinstance(result[0], BaseRecordImpl) @@ -215,6 +217,8 @@ async def test_query_post_filter(self): ) mock_storage.find_all_records.assert_awaited_once_with( type_filter=ARecordImpl.RECORD_TYPE, + order_by=None, + descending=False, tag_query=tag_filter, ) assert result and isinstance(result[0], ARecordImpl) @@ -339,6 +343,8 @@ async def test_query_with_limit(self): tag_query=tag_filter, limit=10, offset=0, + order_by=None, + descending=False, ) assert result and isinstance(result[0], ARecordImpl) assert result[0]._id == record_id @@ -369,6 +375,8 @@ async def test_query_with_offset(self): tag_query=tag_filter, limit=DEFAULT_PAGE_SIZE, offset=10, + order_by=None, + descending=False, ) assert result and isinstance(result[0], ARecordImpl) assert result[0]._id == record_id @@ -399,6 +407,8 @@ async def test_query_with_limit_and_offset(self): tag_query=tag_filter, limit=10, offset=5, + order_by=None, + descending=False, ) assert result and isinstance(result[0], ARecordImpl) assert result[0]._id == record_id @@ -433,7 +443,10 @@ async def test_query_with_limit_and_offset_and_post_filter(self): post_filter_positive={"a": "one"}, ) mock_storage.find_all_records.assert_awaited_once_with( - type_filter=ARecordImpl.RECORD_TYPE, tag_query=tag_filter + type_filter=ARecordImpl.RECORD_TYPE, + order_by=None, + descending=False, + tag_query=tag_filter, ) assert len(result) == 10 assert result and isinstance(result[0], ARecordImpl) diff --git a/acapy_agent/messaging/valid.py b/acapy_agent/messaging/valid.py index cb706deab0..d2b99cfc92 100644 --- a/acapy_agent/messaging/valid.py +++ b/acapy_agent/messaging/valid.py @@ -592,6 +592,21 @@ def __init__(self): ) +class AnonCredsCredRevId(Regexp): + """Validate value against anoncreds credential revocation identifier specification.""" + + EXAMPLE = "12345" + PATTERN = r"^[1-9][0-9]*$" + + def __init__(self): + """Initialize the instance.""" + + super().__init__( + AnonCredsCredRevId.PATTERN, + error="Value {input} is not an anoncreds credential revocation identifier", + ) + + class Predicate(OneOf): """Validate value against predicate.""" @@ -1053,6 +1068,9 @@ def __init__( INDY_CRED_REV_ID_VALIDATE = IndyCredRevId() INDY_CRED_REV_ID_EXAMPLE = IndyCredRevId.EXAMPLE +ANONCREDS_CRED_REV_ID_VALIDATE = AnonCredsCredRevId() +ANONCREDS_CRED_REV_ID_EXAMPLE = AnonCredsCredRevId.EXAMPLE + MAJOR_MINOR_VERSION_VALIDATE = MajorMinorVersion() MAJOR_MINOR_VERSION_EXAMPLE = MajorMinorVersion.EXAMPLE diff --git a/acapy_agent/multitenant/admin/routes.py b/acapy_agent/multitenant/admin/routes.py index c89b302914..3d03b6bf62 100644 --- a/acapy_agent/multitenant/admin/routes.py +++ b/acapy_agent/multitenant/admin/routes.py @@ -16,7 +16,10 @@ from ...core.profile import ProfileManagerProvider from ...messaging.models.base import BaseModelError from ...messaging.models.openapi import OpenAPISchema -from ...messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ...messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ...messaging.valid import UUID4_EXAMPLE, JSONWebToken from ...multitenant.base import BaseMultitenantManager from ...storage.error import StorageError, StorageNotFoundError @@ -381,7 +384,7 @@ async def wallets_list(request: web.BaseRequest): if wallet_name: query["wallet_name"] = wallet_name - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) try: async with profile.session() as session: @@ -390,9 +393,10 @@ async def wallets_list(request: web.BaseRequest): tag_filter=query, limit=limit, offset=offset, + order_by=order_by, + descending=descending, ) results = [format_wallet_record(record) for record in records] - results.sort(key=lambda w: w["created_at"]) except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err diff --git a/acapy_agent/multitenant/admin/tests/test_routes.py b/acapy_agent/multitenant/admin/tests/test_routes.py index 4ec0ba142a..6f24c863de 100644 --- a/acapy_agent/multitenant/admin/tests/test_routes.py +++ b/acapy_agent/multitenant/admin/tests/test_routes.py @@ -87,7 +87,7 @@ async def test_wallets_list(self): ), ] mock_wallet_record.query = mock.CoroutineMock() - mock_wallet_record.query.return_value = [wallets[2], wallets[0], wallets[1]] + mock_wallet_record.query.return_value = wallets await test_module.wallets_list(self.request) mock_response.assert_called_once_with( diff --git a/acapy_agent/protocols/issue_credential/v1_0/routes.py b/acapy_agent/protocols/issue_credential/v1_0/routes.py index 8afe893a5f..d39893a2b0 100644 --- a/acapy_agent/protocols/issue_credential/v1_0/routes.py +++ b/acapy_agent/protocols/issue_credential/v1_0/routes.py @@ -23,7 +23,10 @@ from ....messaging.credential_definitions.util import CRED_DEF_TAGS from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ....messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ....messaging.valid import ( INDY_CRED_DEF_ID_EXAMPLE, INDY_CRED_DEF_ID_VALIDATE, @@ -411,7 +414,7 @@ async def credential_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) try: async with context.profile.session() as session: @@ -420,6 +423,8 @@ async def credential_exchange_list(request: web.BaseRequest): tag_filter=tag_filter, limit=limit, offset=offset, + order_by=order_by, + descending=descending, post_filter_positive=post_filter, ) results = [record.serialize() for record in records] diff --git a/acapy_agent/protocols/issue_credential/v2_0/formats/indy/handler.py b/acapy_agent/protocols/issue_credential/v2_0/formats/indy/handler.py index 19a2857cf3..a1888f317d 100644 --- a/acapy_agent/protocols/issue_credential/v2_0/formats/indy/handler.py +++ b/acapy_agent/protocols/issue_credential/v2_0/formats/indy/handler.py @@ -209,7 +209,7 @@ async def create_offer( if isinstance(self.profile, AskarAnoncredsProfile): raise V20CredFormatError( - "This issuer is anoncreds capable. Please use the anonreds format." + "This issuer is anoncreds capable. Please use the anoncreds format." ) issuer = self.profile.inject(IndyIssuer) diff --git a/acapy_agent/protocols/issue_credential/v2_0/routes.py b/acapy_agent/protocols/issue_credential/v2_0/routes.py index f1517cc0ed..a00e0fe853 100644 --- a/acapy_agent/protocols/issue_credential/v2_0/routes.py +++ b/acapy_agent/protocols/issue_credential/v2_0/routes.py @@ -26,7 +26,10 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ....messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ....messaging.valid import ( ANONCREDS_CRED_DEF_ID_EXAMPLE, ANONCREDS_DID_EXAMPLE, @@ -626,7 +629,7 @@ async def credential_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) try: async with profile.session() as session: @@ -635,6 +638,8 @@ async def credential_exchange_list(request: web.BaseRequest): tag_filter=tag_filter, limit=limit, offset=offset, + order_by=order_by, + descending=descending, post_filter_positive=post_filter, ) diff --git a/acapy_agent/protocols/present_proof/v1_0/routes.py b/acapy_agent/protocols/present_proof/v1_0/routes.py index 97459a687e..5b554e4d5e 100644 --- a/acapy_agent/protocols/present_proof/v1_0/routes.py +++ b/acapy_agent/protocols/present_proof/v1_0/routes.py @@ -11,6 +11,7 @@ response_schema, ) from marshmallow import fields, validate +from marshmallow.validate import Range from ....admin.decorators.auth import tenant_authentication from ....admin.request_context import AdminRequestContext @@ -25,7 +26,10 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ....messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, @@ -36,6 +40,7 @@ UUID4_EXAMPLE, UUID4_VALIDATE, ) +from ....storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event from ....wallet.error import WalletNotFoundError @@ -237,21 +242,35 @@ class CredentialsFetchQueryStringSchema(OpenAPISchema): ) start = fields.Str( required=False, + load_default="0", validate=NUM_STR_WHOLE_VALIDATE, metadata={ - "description": "Start index", + "description": "Start index (DEPRECATED - use offset instead)", "strict": True, "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, }, ) count = fields.Str( required=False, + load_default="10", validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=Range(min=1, max=MAXIMUM_PAGE_SIZE), + metadata={"description": "Number of results to return", "example": 50}, + ) + offset = fields.Int( + required=False, + validate=Range(min=0), + metadata={"description": "Offset for pagination", "example": 0}, + ) extra_query = fields.Str( required=False, validate=INDY_EXTRA_WQL_VALIDATE, @@ -309,7 +328,7 @@ async def presentation_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) try: async with context.profile.session() as session: @@ -318,6 +337,8 @@ async def presentation_exchange_list(request: web.BaseRequest): tag_filter=tag_filter, limit=limit, offset=offset, + order_by=order_by, + descending=descending, post_filter_positive=post_filter, ) results = [record.serialize() for record in records] @@ -413,25 +434,29 @@ async def presentation_exchange_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = request.query.get("start") - count = request.query.get("count") + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" extra_query = json.loads(encoded_extra_query) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - holder = profile.inject(IndyHolder) try: credentials = await holder.get_credentials_for_presentation_request_by_referent( pres_ex_record._presentation_request.ser, presentation_referents, - start, - count, - extra_query, + offset=offset, + limit=limit, + extra_query=extra_query, ) except IndyHolderError as err: if pres_ex_record: diff --git a/acapy_agent/protocols/present_proof/v2_0/routes.py b/acapy_agent/protocols/present_proof/v2_0/routes.py index 2401ee5c14..1aef1b9bb2 100644 --- a/acapy_agent/protocols/present_proof/v2_0/routes.py +++ b/acapy_agent/protocols/present_proof/v2_0/routes.py @@ -12,6 +12,7 @@ response_schema, ) from marshmallow import ValidationError, fields, validate, validates_schema +from marshmallow.validate import Range from ....admin.decorators.auth import tenant_authentication from ....admin.request_context import AdminRequestContext @@ -27,7 +28,10 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset +from ....messaging.models.paginated_query import ( + PaginatedQuerySchema, + get_paginated_query_params, +) from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, @@ -38,16 +42,16 @@ UUID4_EXAMPLE, UUID4_VALIDATE, ) -from ....storage.base import BaseStorage +from ....storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE, BaseStorage from ....storage.error import StorageError, StorageNotFoundError from ....storage.vc_holder.base import VCHolder from ....storage.vc_holder.vc_record import VCRecord from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event from ....vc.ld_proofs import ( BbsBlsSignature2020, + EcdsaSecp256r1Signature2019, Ed25519Signature2018, Ed25519Signature2020, - EcdsaSecp256r1Signature2019, ) from ....wallet.error import WalletNotFoundError from ..dif.pres_exch import ClaimFormat, InputDescriptors, SchemaInputDescriptor @@ -367,21 +371,35 @@ class V20CredentialsFetchQueryStringSchema(OpenAPISchema): ) start = fields.Str( required=False, + load_default="0", validate=NUM_STR_WHOLE_VALIDATE, metadata={ - "description": "Start index", + "description": "Start index (DEPRECATED - use offset instead)", "strict": True, "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, }, ) count = fields.Str( required=False, + load_default="10", validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=Range(min=1, max=MAXIMUM_PAGE_SIZE), + metadata={"description": "Number of results to return", "example": 50}, + ) + offset = fields.Int( + required=False, + validate=Range(min=0), + metadata={"description": "Offset for pagination", "example": 0}, + ) extra_query = fields.Str( required=False, validate=INDY_EXTRA_WQL_VALIDATE, @@ -468,7 +486,7 @@ async def present_proof_list(request: web.BaseRequest): if request.query.get(k, "") != "" } - limit, offset = get_limit_offset(request) + limit, offset, order_by, descending = get_paginated_query_params(request) try: async with profile.session() as session: @@ -477,6 +495,8 @@ async def present_proof_list(request: web.BaseRequest): tag_filter=tag_filter, limit=limit, offset=offset, + order_by=order_by, + descending=descending, post_filter_positive=post_filter, ) results = [record.serialize() for record in records] @@ -564,17 +584,21 @@ async def present_proof_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = request.query.get("start") - count = request.query.get("count") + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" extra_query = json.loads(encoded_extra_query) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - wallet_type = profile.settings.get_value("wallet.type") if wallet_type == "askar-anoncreds": holder = AnonCredsHolder(profile) @@ -596,9 +620,9 @@ async def present_proof_credentials_list(request: web.BaseRequest): await holder.get_credentials_for_presentation_request_by_referent( pres_request, pres_referents, - start, - count, - extra_query, + offset=offset, + limit=limit, + extra_query=extra_query, ) ) @@ -776,7 +800,7 @@ async def present_proof_credentials_list(request: web.BaseRequest): search = dif_holder.search_credentials( proof_types=proof_type, pd_uri_list=uri_group ) - cred_group = await search.fetch(count) + cred_group = await search.fetch(limit) ( cred_group_vcrecord_list, cred_group_vcrecord_ids_set, @@ -790,7 +814,7 @@ async def present_proof_credentials_list(request: web.BaseRequest): proof_types=proof_type, pd_uri_list=uri_list, ) - records = await search.fetch(count) + records = await search.fetch(limit) # Avoiding addition of duplicate records vcrecord_list, vcrecord_ids_set = await process_vcrecords_return_list( records, record_ids diff --git a/acapy_agent/resolver/__init__.py b/acapy_agent/resolver/__init__.py index 8ebc39028b..55c4a39f08 100644 --- a/acapy_agent/resolver/__init__.py +++ b/acapy_agent/resolver/__init__.py @@ -5,6 +5,7 @@ from ..config.injection_context import InjectionContext from ..config.provider import ClassProvider from ..resolver.did_resolver import DIDResolver +from .base import ResolverError LOGGER = logging.getLogger(__name__) @@ -56,11 +57,14 @@ async def setup(context: InjectionContext): registry.register_resolver(webvh_resolver) if context.settings.get("resolver.universal"): - universal_resolver = ClassProvider( - "acapy_agent.resolver.default.universal.UniversalResolver" - ).provide(context.settings, context.injector) - await universal_resolver.setup(context) - registry.register_resolver(universal_resolver) + try: + universal_resolver = ClassProvider( + "acapy_agent.resolver.default.universal.UniversalResolver" + ).provide(context.settings, context.injector) + await universal_resolver.setup(context) + registry.register_resolver(universal_resolver) + except ResolverError as err: + LOGGER.warning(f"Universal Resolver setup failed: {err}") peer_did_1_resolver = ClassProvider( "acapy_agent.resolver.default.peer1.PeerDID1Resolver" diff --git a/acapy_agent/resolver/default/universal.py b/acapy_agent/resolver/default/universal.py index 8d4b1eb1d8..1cc626e758 100644 --- a/acapy_agent/resolver/default/universal.py +++ b/acapy_agent/resolver/default/universal.py @@ -103,12 +103,15 @@ async def _resolve( async def _fetch_resolver_props(self) -> dict: """Retrieve universal resolver properties.""" async with aiohttp.ClientSession(headers=self.__default_headers) as session: - async with session.get(f"{self._endpoint}/properties/") as resp: - if 200 <= resp.status < 400: - return await resp.json() - raise ResolverError( - "Failed to retrieve resolver properties: " + await resp.text() - ) + try: + async with session.get(f"{self._endpoint}/properties/") as resp: + if 200 <= resp.status < 400: + return await resp.json() + raise ResolverError( + "Failed to retrieve resolver properties: " + await resp.text() + ) + except aiohttp.ClientError as err: + raise ResolverError(f"Failed to fetch resolver properties: {err}") async def _get_supported_did_regex(self): props = await self._fetch_resolver_props() diff --git a/acapy_agent/revocation_anoncreds/routes.py b/acapy_agent/revocation_anoncreds/routes.py index ccda33a5f1..145074ab29 100644 --- a/acapy_agent/revocation_anoncreds/routes.py +++ b/acapy_agent/revocation_anoncreds/routes.py @@ -29,6 +29,8 @@ from ..anoncreds.routes import ( create_transaction_for_endorser_description, endorser_connection_id_description, + AnonCredsRevRegIdMatchInfoSchema, + AnoncredsRevocationModuleResponseSchema, ) from ..askar.profile_anon import AskarAnoncredsProfile from ..indy.issuer import IndyIssuerError @@ -40,8 +42,8 @@ from ..messaging.valid import ( ANONCREDS_CRED_DEF_ID_EXAMPLE, ANONCREDS_CRED_DEF_ID_VALIDATE, - INDY_CRED_REV_ID_EXAMPLE, - INDY_CRED_REV_ID_VALIDATE, + ANONCREDS_CRED_REV_ID_EXAMPLE, + ANONCREDS_CRED_REV_ID_VALIDATE, ANONCREDS_REV_REG_ID_EXAMPLE, ANONCREDS_REV_REG_ID_VALIDATE, UUID4_EXAMPLE, @@ -68,10 +70,6 @@ TAG_TITLE = "anoncreds - revocation" -class RevocationAnoncredsModuleResponseSchema(OpenAPISchema): - """Response schema for Revocation Module.""" - - class RevRegResultSchemaAnoncreds(OpenAPISchema): """Result schema for revocation registry creation request.""" @@ -107,10 +105,10 @@ def validate_fields(self, data, **kwargs): ) cred_rev_id = fields.Str( required=False, - validate=INDY_CRED_REV_ID_VALIDATE, + validate=ANONCREDS_CRED_REV_ID_VALIDATE, metadata={ "description": "Credential revocation identifier", - "example": INDY_CRED_REV_ID_EXAMPLE, + "example": ANONCREDS_CRED_REV_ID_EXAMPLE, }, ) cred_ex_id = fields.Str( @@ -279,19 +277,6 @@ class SetRevRegStateQueryStringSchema(OpenAPISchema): ) -class RevRegIdMatchInfoSchema(OpenAPISchema): - """Path parameters and validators for request taking rev reg id.""" - - rev_reg_id = fields.Str( - required=True, - validate=ANONCREDS_REV_REG_ID_VALIDATE, - metadata={ - "description": "Revocation Registry identifier", - "example": ANONCREDS_REV_REG_ID_EXAMPLE, - }, - ) - - class RevocationCredDefIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking cred def id.""" @@ -351,10 +336,10 @@ class PublishRevocationsSchemaAnoncreds(OpenAPISchema): keys=fields.Str(metadata={"example": ANONCREDS_REV_REG_ID_EXAMPLE}), values=fields.List( fields.Str( - validate=INDY_CRED_REV_ID_VALIDATE, + validate=ANONCREDS_CRED_REV_ID_VALIDATE, metadata={ "description": "Credential revocation identifier", - "example": INDY_CRED_REV_ID_EXAMPLE, + "example": ANONCREDS_CRED_REV_ID_EXAMPLE, }, ) ), @@ -371,10 +356,10 @@ class PublishRevocationsResultSchemaAnoncreds(OpenAPISchema): keys=fields.Str(metadata={"example": ANONCREDS_REV_REG_ID_EXAMPLE}), values=fields.List( fields.Str( - validate=INDY_CRED_REV_ID_VALIDATE, + validate=ANONCREDS_CRED_REV_ID_VALIDATE, metadata={ "description": "Credential revocation identifier", - "example": INDY_CRED_REV_ID_EXAMPLE, + "example": ANONCREDS_CRED_REV_ID_EXAMPLE, }, ) ), @@ -455,7 +440,7 @@ def validate_fields(self, data, **kwargs): summary="Revoke an issued credential", ) @request_schema(RevokeRequestSchemaAnoncreds()) -@response_schema(RevocationAnoncredsModuleResponseSchema(), description="") +@response_schema(AnoncredsRevocationModuleResponseSchema(), description="") @tenant_authentication async def revoke(request: web.BaseRequest): """Request handler for storing a credential revocation. @@ -585,7 +570,7 @@ async def get_rev_regs(request: web.BaseRequest): tags=[TAG_TITLE], summary="Get revocation registry by revocation registry id", ) -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @response_schema(RevRegResultSchemaAnoncreds(), 200, description="") @tenant_authentication async def get_rev_reg(request: web.BaseRequest): @@ -723,7 +708,7 @@ async def rotate_rev_reg(request: web.BaseRequest): tags=[TAG_TITLE], summary="Get number of credentials issued against revocation registry", ) -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @response_schema(RevRegIssuedResultSchemaAnoncreds(), 200, description="") @tenant_authentication async def get_rev_reg_issued_count(request: web.BaseRequest): @@ -764,7 +749,7 @@ async def get_rev_reg_issued_count(request: web.BaseRequest): tags=[TAG_TITLE], summary="Get details of credentials issued against revocation registry", ) -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @response_schema(CredRevRecordDetailsResultSchemaAnoncreds(), 200, description="") @tenant_authentication async def get_rev_reg_issued(request: web.BaseRequest): @@ -806,7 +791,7 @@ async def get_rev_reg_issued(request: web.BaseRequest): tags=[TAG_TITLE], summary="Get details of revoked credentials from ledger", ) -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @response_schema(CredRevIndyRecordsResultSchemaAnoncreds(), 200, description="") @tenant_authentication async def get_rev_reg_indy_recs(request: web.BaseRequest): @@ -851,7 +836,7 @@ async def get_rev_reg_indy_recs(request: web.BaseRequest): tags=[TAG_TITLE], summary="Fix revocation state in wallet and return number of updated entries", ) -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @querystring_schema(RevRegUpdateRequestMatchInfoSchema()) @response_schema(RevRegWalletUpdatedResultSchemaAnoncreds(), 200, description="") @tenant_authentication @@ -992,8 +977,8 @@ async def get_cred_rev_record(request: web.BaseRequest): summary="Download tails file", produces=["application/octet-stream"], ) -@match_info_schema(RevRegIdMatchInfoSchema()) -@response_schema(RevocationAnoncredsModuleResponseSchema, description="tails file") +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) +@response_schema(AnoncredsRevocationModuleResponseSchema, description="tails file") @tenant_authentication async def get_tails_file(request: web.BaseRequest) -> web.FileResponse: """Request handler to download tails file for revocation registry. @@ -1030,7 +1015,7 @@ async def get_tails_file(request: web.BaseRequest) -> web.FileResponse: @docs(tags=[TAG_TITLE], summary="Set revocation registry state manually") -@match_info_schema(RevRegIdMatchInfoSchema()) +@match_info_schema(AnonCredsRevRegIdMatchInfoSchema()) @querystring_schema(SetRevRegStateQueryStringSchema()) @response_schema(RevRegResultSchemaAnoncreds(), 200, description="") @tenant_authentication diff --git a/acapy_agent/revocation_anoncreds/tests/test_routes.py b/acapy_agent/revocation_anoncreds/tests/test_routes.py index ae8d806b1a..40f2edda3e 100644 --- a/acapy_agent/revocation_anoncreds/tests/test_routes.py +++ b/acapy_agent/revocation_anoncreds/tests/test_routes.py @@ -43,7 +43,7 @@ async def test_validate_cred_rev_rec_qs_and_revoke_req(self): req.validate_fields( { "rev_reg_id": test_module.ANONCREDS_REV_REG_ID_EXAMPLE, - "cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE, + "cred_rev_id": test_module.ANONCREDS_CRED_REV_ID_EXAMPLE, } ) req.validate_fields({"cred_ex_id": test_module.UUID4_EXAMPLE}) @@ -54,7 +54,9 @@ async def test_validate_cred_rev_rec_qs_and_revoke_req(self): {"rev_reg_id": test_module.ANONCREDS_REV_REG_ID_EXAMPLE} ) with self.assertRaises(test_module.ValidationError): - req.validate_fields({"cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE}) + req.validate_fields( + {"cred_rev_id": test_module.ANONCREDS_CRED_REV_ID_EXAMPLE} + ) with self.assertRaises(test_module.ValidationError): req.validate_fields( { @@ -65,7 +67,7 @@ async def test_validate_cred_rev_rec_qs_and_revoke_req(self): with self.assertRaises(test_module.ValidationError): req.validate_fields( { - "cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE, + "cred_rev_id": test_module.ANONCREDS_CRED_REV_ID_EXAMPLE, "cred_ex_id": test_module.UUID4_EXAMPLE, } ) @@ -73,7 +75,7 @@ async def test_validate_cred_rev_rec_qs_and_revoke_req(self): req.validate_fields( { "rev_reg_id": test_module.ANONCREDS_REV_REG_ID_EXAMPLE, - "cred_rev_id": test_module.INDY_CRED_REV_ID_EXAMPLE, + "cred_rev_id": test_module.ANONCREDS_CRED_REV_ID_EXAMPLE, "cred_ex_id": test_module.UUID4_EXAMPLE, } ) diff --git a/acapy_agent/storage/askar.py b/acapy_agent/storage/askar.py index a3480c2ef6..607c6b927f 100644 --- a/acapy_agent/storage/askar.py +++ b/acapy_agent/storage/askar.py @@ -174,6 +174,8 @@ async def find_paginated_records( tag_query: Optional[Mapping] = None, limit: int = DEFAULT_PAGE_SIZE, offset: int = 0, + order_by: Optional[str] = None, + descending: bool = False, ) -> Sequence[StorageRecord]: """Retrieve a page of records matching a particular type filter and tag query. @@ -182,6 +184,11 @@ async def find_paginated_records( tag_query: An optional dictionary of tag filter clauses limit: The maximum number of records to retrieve offset: The offset to start retrieving records from + order_by: An optional field by which to order the records. + descending: Whether to order the records in descending order. + + Returns: + A sequence of StorageRecord matching the filter and query parameters. """ results = [] @@ -190,6 +197,8 @@ async def find_paginated_records( tag_filter=tag_query, limit=limit, offset=offset, + order_by=order_by, + descending=descending, profile=self._session.profile.settings.get("wallet.askar_profile"), ): results += ( @@ -206,13 +215,19 @@ async def find_all_records( self, type_filter: str, tag_query: Optional[Mapping] = None, + order_by: Optional[str] = None, + descending: bool = False, options: Optional[Mapping] = None, ): """Retrieve all records matching a particular type filter and tag query.""" for_update = bool(options and options.get("forUpdate")) results = [] for row in await self._session.handle.fetch_all( - type_filter, tag_query, for_update=for_update + category=type_filter, + tag_filter=tag_query, + order_by=order_by, + descending=descending, + for_update=for_update, ): results.append( StorageRecord( diff --git a/acapy_agent/storage/base.py b/acapy_agent/storage/base.py index d1cbc8086e..e065c32b88 100644 --- a/acapy_agent/storage/base.py +++ b/acapy_agent/storage/base.py @@ -99,6 +99,8 @@ async def find_paginated_records( tag_query: Optional[Mapping] = None, limit: int = DEFAULT_PAGE_SIZE, offset: int = 0, + order_by: Optional[str] = None, + descending: bool = False, ) -> Sequence[StorageRecord]: """Retrieve a page of records matching a particular type filter and tag query. @@ -107,6 +109,11 @@ async def find_paginated_records( tag_query: An optional dictionary of tag filter clauses limit: The maximum number of records to retrieve offset: The offset to start retrieving records from + order_by: An optional field by which to order the records. + descending: Whether to order the records in descending order. + + Returns: + A sequence of StorageRecord matching the filter and query parameters. """ @abstractmethod @@ -114,6 +121,8 @@ async def find_all_records( self, type_filter: str, tag_query: Optional[Mapping] = None, + order_by: Optional[str] = None, + descending: bool = False, options: Optional[Mapping] = None, ) -> Sequence[StorageRecord]: """Retrieve all records matching a particular type filter and tag query. @@ -121,6 +130,8 @@ async def find_all_records( Args: type_filter: The type of records to filter by. tag_query: An optional dictionary of tag filter clauses. + order_by: An optional field by which to order the records. + descending: Whether to order the records in descending order. options: Additional options for the query. """ diff --git a/demo/docker-agent/Dockerfile.acapy b/demo/docker-agent/Dockerfile.acapy index 0fad59f419..7a4c35f3d5 100644 --- a/demo/docker-agent/Dockerfile.acapy +++ b/demo/docker-agent/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0 +FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2 USER root diff --git a/demo/multi-demo/Dockerfile.acapy b/demo/multi-demo/Dockerfile.acapy index 0fad59f419..7a4c35f3d5 100644 --- a/demo/multi-demo/Dockerfile.acapy +++ b/demo/multi-demo/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0 +FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2 USER root diff --git a/demo/playground/Dockerfile.acapy b/demo/playground/Dockerfile.acapy index 5dfa08027a..6c0e56205d 100644 --- a/demo/playground/Dockerfile.acapy +++ b/demo/playground/Dockerfile.acapy @@ -1,4 +1,4 @@ -FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0 +FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2 USER root diff --git a/demo/playground/README.md b/demo/playground/README.md index 6608418b4d..19ed6d8b4d 100644 --- a/demo/playground/README.md +++ b/demo/playground/README.md @@ -26,7 +26,7 @@ These configuration files are provided to the ACA-Py start command via the `AGEN ### Dockerfile and start.sh -[`Dockerfile.acapy`](./Dockerfile.acapy) assembles the image to run. Currently based on [ACA-Py 1.2.0](ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0), we need [jq](https://stedolan.github.io/jq/) to setup (or not) the ngrok tunnel and execute the Aca-py start command - see [`start.sh`](./start.sh). You may note that the start command is very sparse, additional configuration is done via environment variables in the [docker compose file](./docker-compose.yml). +[`Dockerfile.acapy`](./Dockerfile.acapy) assembles the image to run. Currently based on [ACA-Py 1.2.0](ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2), we need [jq](https://stedolan.github.io/jq/) to setup (or not) the ngrok tunnel and execute the Aca-py start command - see [`start.sh`](./start.sh). You may note that the start command is very sparse, additional configuration is done via environment variables in the [docker compose file](./docker-compose.yml). ### ngrok diff --git a/docker/Dockerfile b/docker/Dockerfile index 101cffe8f5..468d72cb28 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ ARG python_version=3.12 -FROM python:${python_version}-slim-bullseye AS build +FROM python:${python_version}-slim-bookworm AS build WORKDIR /src @@ -9,7 +9,7 @@ COPY ./pyproject.toml ./poetry.lock ./README.md ./ RUN pip install --no-cache-dir poetry==1.8.3 RUN poetry build -FROM python:${python_version}-slim-bullseye AS main +FROM python:${python_version}-slim-bookworm AS main ARG uid=1001 ARG user=aries @@ -46,24 +46,17 @@ RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ apt-transport-https \ ca-certificates \ - build-essential \ - bzip2 \ curl \ - git \ - less \ libffi-dev \ libgmp10 \ - liblzma5 \ libncurses5 \ libncursesw5 \ - libsecp256k1-0 \ - libzmq5 \ - net-tools \ openssl \ sqlite3 \ zlib1g && \ rm -rf /var/lib/apt/lists/* /usr/share/doc/* + WORKDIR $HOME # Add local binaries and aliases to path @@ -103,7 +96,7 @@ RUN acapy_agent_package=$(find ./ -name "acapy_agent*.whl" | head -n 1) && \ # Clean-up unnecessary build dependencies and reduce final image size USER root -RUN apt-get purge -y --auto-remove build-essential +RUN apt-get purge -y USER $user diff --git a/docker/Dockerfile.demo b/docker/Dockerfile.demo index dfbf170e55..10ca60c06f 100644 --- a/docker/Dockerfile.demo +++ b/docker/Dockerfile.demo @@ -1,4 +1,4 @@ -ARG from_image=ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0 +ARG from_image=ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2 FROM ${from_image} ENV ENABLE_PTVSD 0 diff --git a/docker/Dockerfile.run b/docker/Dockerfile.run index fe96405184..d41de27d19 100644 --- a/docker/Dockerfile.run +++ b/docker/Dockerfile.run @@ -1,5 +1,5 @@ ARG python_version=3.12.4 -FROM python:3.12-slim-bullseye +FROM python:3.12-slim-bookworm RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index 7b597c2785..eeb6108ea3 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -1,5 +1,5 @@ ARG python_version=3.12.4 -FROM python:${python_version}-slim-bullseye +FROM python:${python_version}-slim-bookworm RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ diff --git a/docs/design/DIDManagement.md b/docs/design/DIDManagement.md new file mode 100644 index 0000000000..3ec00e89b2 --- /dev/null +++ b/docs/design/DIDManagement.md @@ -0,0 +1,186 @@ +# DID Management: Updates to DID and Key Storage + +## Introduction + +As part of our initiative to support a wider range of DID Methods in ACA-Py, we need to update the primitives related to DID and key storage within ACA-Py. This document outlines the proposed changes to how DIDs and keys are stored and managed, the rationale behind these changes, and the migration strategy from the current implementation. + +## Background + +### Askar Records and Lookup Mechanisms + +[Askar](https://github.com/openwallet-foundation/askar) is the secure storage solution used by ACA-Py. Askar encrypts all data and provides a tagging mechanism to enable lookup of encrypted records. An entry in Askar is composed of the following elements: + +- **Category:** The major group or "bucket" that the entry belongs to. +- **Name:** The primary identifier for the record; this is roughly equivalent to primary keys on a traditional DB table. The most efficient lookup possible is by name. +- **Value:** The value stored in the entry. This is usually a serialized JSON object. +- **Tags:** A mapping of strings to strings or lists of strings. These values can be used with the ["Wallet Query Language (WQL)"](https://github.com/hyperledger-archives/indy-sdk/tree/main/docs/design/011-wallet-query-language) to look up encrypted Askar entries efficiently. + +Askar has a dedicated API for storage and retrieval of keys. However, this API is conceptually just a shorthand for record storage and retrieval from a "private" `key` category with the key itself as the value of the entry. Key entries behave almost exactly the same as non-key entries, including names and tags. + +### Current State of DID Storage + +At present, the `DIDInfo` class in ACA-Py is structured as follows: + +```python +DIDInfo = NamedTuple( + "DIDInfo", + [ + ("did", str), + ("verkey", str), + ("metadata", dict), + ("method", DIDMethod), + ("key_type", KeyType), + ], +) +``` + +When stored in Askar, DID records have the following characteristics: + +- **Category:** did +- **Name:** the value of the did, e.g. `did:example:123`. For Indy/did:sov, the value is the nym, e.g. `As728S9715ppSToDurKnvT` +- **Value:** a JSON object with the following attributes: + - `did`: the DID (or nym) + - `method`: the method name, e.g. `peer` + - `verkey`: the base58 encoding of the public key associated with this DID + - `verkey_type`: the key type of the verkey, e.g. `ed25519` + - `metadata`: A container for arbitrary metadata. In practice, the following values are inserted into metadata: + - `posted`: a boolean value representing whether this DID has been published to an indy network + - `endpoint`: a string value representing the endpoint attrib of this DID on an indy network +- **Tags:** + - `method`: the method name, e.g. `peer` + - `verkey`: the base58 encoding of the public key associated with this DID + - `verkey_type`: the key type of the verkey, e.g. `ed25519` + +### Current State of Key Storage + +Keys are managed by using the "verkey" as the name of the Askar record. Operations like signing or encrypting DIDComm messages retrieve the key by verkey. Usually, when initiating a cryptographic operation, the key is looked up by first retrieving the `DIDInfo` object by DID (or by nym) from the wallet and then the `DIDInfo.verkey` value is used to retrieve the key. + +### Limitations + +- **Indy-Centric Design:** The current structure is closely tied to Indy, making it less suitable for other DID Methods. +- **Single Key per DID:** Assumes a one-to-one relationship between DIDs and keys, which is not the case for many DID Methods. +- **Inefficient for Multiple Keys:** Lacks support for DIDs with multiple verification methods and keys. + +## Proposed Updates + +### Goals + +- **Support Multiple Keys per DID:** Allow DIDs to have multiple associated keys and verification methods. +- **Method-Agnostic Design:** Create a storage structure that supports various DID Methods beyond Indy. +- **Efficient Key Retrieval:** Enable efficient lookup of keys based on DIDs, verification method IDs, verification relationships, and key types. + +### Key Storage + +#### Unbound Keys + +Unbound keys are keys not (or not yet) associated with a specific DID. + +Some DID Methods require knowledge of key material prior to creation of the DID. For example, in did:peer, the key material contributes directly to the formation of the DID itself. Unbound keys enable us to create and reference the key material during this early phase of DID creation for methods such as did:peer. + +Additionally, there may be use cases that we have not yet identified that may be best served by creating and referencing keys not associated with a DID at all. Unbound keys serve this purpose as well. + +- **Category:** `key` +- **Name:** Multikey representation (e.g., `z6Mkw...`) +- **Value:** The key material (private or public key) +- **Tags:** + - `KeyAlg`: Implicit tag indicating the key algorithm (e.g., `ed25519`) + - `alias`: A list of human-friendly aliases for the key + +#### Bound Keys + +Bound keys are associated with a specific DID. + +When an unbound key is used to generate a DID for a method like did:peer, bound key representations MUST be stored in the wallet. The unbound representation MAY be removed after the bound representation is added. + +- **Category:** `key` +- **Name:** Verification Method ID (absolute DID URL, e.g., `did:example:123#key-1`) +- **Value:** The key material +- **Tags:** + - `KeyAlg`: Implicit tag indicating the key algorithm (e.g. `ed25519`) + - `did`: The DID associated with the key + - `rel`: A list of verification relationships (e.g., `["authentication", "assertionMethod"]`) + +With this structure and tagging mechanism, we achieve direct retrieval without additional lookups when using verification method ID; efficient querying based on DID, purpose, and key type; and a single key supporting multiple verification relationships. + +#### DIDComm v1 Keys + +The DIDComm v1 stack is of sufficient complexity that it is necessary to make accommodations for it to continue operating more or less unchanged. To do this, any keys intended for use as a DIDComm v1 sender or receiver must also be stored in the following way: + +- **Category:** `key` +- **Name:** verkey or base58 ed25519 public key +- **Value:** The key material (private or public key) +- **Tags:** + - `KeyAlg`: Implicit tag indicating the key algorithm; for DIDComm v1 keys, this will always be `ed25519` + - `did`: The DID this key is associated with + +In DIDComm v1, it is required that the X25519 key used to perform key agreement will always be derived from the Ed25519 verkey. This X25519 key may be represented in bound keys for a DID but it MUST be the key derived from the Ed25519 key. + +### DID Storage + +With the Key storage updates, the DID records become less significant of a construct; rather than looking up a DID and then looking up a key, the usual pattern will be to look up a key directly with the DID value being used as a tag filter. + +- **Category:** did +- **Name:** the value of the did, e.g. `did:example:123` (no "unqualified" DIDs allowed) +- **Value:** a JSON object with the following attributes: + - `method`: the method name, e.g. `peer` + - `metadata`: A container for arbitrary metadata; the DID Method implementation may determine what, if any, metadata is used +- **Tags:** + - `method`: the method name, e.g. `peer` + +### `Wallet.get_local_did_for_verkey` + +This method looks up a DID we own by "verkey." This method is used to: + +1. Associate an inbound message with a connection (`BaseConnectionManager.resolve_inbound_connection`) +2. Look up a connection based on recipient key of a mediation key list update (`RouteManager.connection_from_recipient_key`) + - This method currently has issues. See #2818. +3. Apply a verkey filter on the `GET /wallet/did` Admin API Endpoint + +For use cases 1 and 2, this should use the DIDComm v1 key record and return DID info based off the associated `did` tag. + +For use case 3, filtering by verkey when listing DIDs should be deprecated. + +### Nym Storage + +To continue supporting Legacy Indy (i.e. Not did:indy), a new Nym record should be added. + +- **Category:** nym +- **Name:** the value of the nym, e.g. `As728S9715ppSToDurKnvT` +- **Value:** a JSON object with the following attributes: + - `nym`: the nym + - `verkey`: the base58 encoding of the public key associated with this nym + - `metadata`: A container for arbitrary metadata. In practice, the following values are inserted into metadata: + - `posted`: a boolean value representing whether this DID has been published to an indy network + - `endpoint`: a string value representing the endpoint attrib of this DID on an indy network +- **Tags:** + - `verkey`: the base58 encoding of the public key associated with this DID + +This record looks essentially the same as the previous DID record but simplified to remove past attempts to make DID records better support various DID Methods. + +All Indy operations that depend on retrieving a `DIDInfo` object should be updated to retrieve a `Nym` object. + +In the past, the term "DID" was used to describe what is more accurately a "Nym." Expectations about how the "DID" could be used, what keys were (or were not) capable of being associated with it, whether the "DID" was published to a public location or not, etc. were really limitations and expectations that apply uniquely to nyms. By making this distinction between DIDs and Nyms, support for Legacy Indy and support for new DID Methods should be able to coexist more harmoniously. + +## Migration Strategy + +To transition from the current storage model to the proposed one, we need to migrate existing data and ensure backward compatibility. + +### Migrating `did:sov` DIDs that have been posted + +- Duplicate all existing `did:sov` records into the new `nym` category, mapping attributes and tags appropriately. +- Create a bound key record for every `verkey`, using `did:sov:#key-1` as the verification method ID. + - The key's `rel` tag must include at least `authentication` and `assertionMethod` +- Update the existing key record, identified by `verkey`, to include the `did` tag; this will become the DIDComm v1 key record. +- Update all DIDs to be fully qualified by adding `did:sov:` prefix +- Update all DID records to the new data model + +### Migrating "unqualified peer DIDs" + +- Replace the DID record with a `did:peer:4` short form DID constructed using the `verkey`. + - Service endpoints MAY be excluded; this DID record will only be used for DIDComm v1 communication and the other end of the connection need not and will never know that we changed how we view the DID. +- Create a bound key record for the `verkey`, using the verification method ID used in the `did:peer:4` generation. +- Update the existing key record to include the `did` tag; this will become the DIDComm v1 key record. + +### Migrating DIDs of other Methods + +> TODO diff --git a/docs/features/DIDResolution.md b/docs/features/DIDResolution.md index e7da6ab1a0..224bf1d515 100644 --- a/docs/features/DIDResolution.md +++ b/docs/features/DIDResolution.md @@ -176,7 +176,7 @@ plugin: The following is a fully functional Dockerfile encapsulating this setup: ```dockerfile= -FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.0 +FROM ghcr.io/openwallet-foundation/acapy-agent:py3.12-1.2.2 RUN pip3 install git+https://github.com/dbluhm/acapy-resolver-github CMD ["aca-py", "start", "-it", "http", "0.0.0.0", "3000", "-ot", "http", "-e", "http://localhost:3000", "--admin", "0.0.0.0", "3001", "--admin-insecure-mode", "--no-ledger", "--plugin", "acapy_resolver_github"] diff --git a/docs/requirements.txt b/docs/requirements.txt index 53a0859413..f03925f7c4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,4 +2,4 @@ sphinx==5.3.0 sphinx_rtd_theme==1.3.0 readthedocs-sphinx-search==0.3.2 -sphinx-notfound-page==1.0.4 +sphinx-notfound-page==1.1.0 diff --git a/mkdocs-requirements.txt b/mkdocs-requirements.txt index c1104cc30b..fb7739d721 100644 --- a/mkdocs-requirements.txt +++ b/mkdocs-requirements.txt @@ -1,3 +1,3 @@ -mkdocs-material==9.5.50 +mkdocs-material==9.6.3 mike==2.1.3 diff --git a/open-api/openapi.json b/open-api/openapi.json index 06966bba64..bb10b0ea7d 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2054,18 +2054,34 @@ "/credentials" : { "get" : { "parameters" : [ { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { + "default" : "10", "pattern" : "^[1-9][0-9]*$", "type" : "string" } }, { - "description" : "Start index", + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Offset for pagination", + "in" : "query", + "name" : "offset", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Start index (DEPRECATED - use offset instead)", "in" : "query", "name" : "start", "schema" : { + "default" : "0", "pattern" : "^[0-9]*$", "type" : "string" } @@ -2096,31 +2112,6 @@ }, "/credentials/w3c" : { "post" : { - "parameters" : [ { - "description" : "Maximum number to retrieve", - "in" : "query", - "name" : "count", - "schema" : { - "pattern" : "^[1-9][0-9]*$", - "type" : "string" - } - }, { - "description" : "Start index", - "in" : "query", - "name" : "start", - "schema" : { - "pattern" : "^[0-9]*$", - "type" : "string" - } - }, { - "description" : "(JSON) WQL query", - "in" : "query", - "name" : "wql", - "schema" : { - "pattern" : "^{.*}$", - "type" : "string" - } - } ], "requestBody" : { "content" : { "*/*" : { @@ -4923,10 +4914,11 @@ "type" : "string" } }, { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { + "default" : "10", "pattern" : "^[1-9][0-9]*$", "type" : "string" } @@ -4938,6 +4930,20 @@ "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", "type" : "string" } + }, { + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Offset for pagination", + "in" : "query", + "name" : "offset", + "schema" : { + "type" : "integer" + } }, { "description" : "Proof request referents of interest, comma-separated", "in" : "query", @@ -4946,10 +4952,11 @@ "type" : "string" } }, { - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "in" : "query", "name" : "start", "schema" : { + "default" : "0", "pattern" : "^[0-9]*$", "type" : "string" } @@ -5341,10 +5348,11 @@ "type" : "string" } }, { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { + "default" : "10", "pattern" : "^[1-9][0-9]*$", "type" : "string" } @@ -5356,6 +5364,20 @@ "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", "type" : "string" } + }, { + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Offset for pagination", + "in" : "query", + "name" : "offset", + "schema" : { + "type" : "integer" + } }, { "description" : "Proof request referents of interest, comma-separated", "in" : "query", @@ -5364,10 +5386,11 @@ "type" : "string" } }, { - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "in" : "query", "name" : "start", "schema" : { + "default" : "0", "pattern" : "^[0-9]*$", "type" : "string" } diff --git a/open-api/swagger.json b/open-api/swagger.json index 0ef90cd209..26396f8bac 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1718,16 +1718,30 @@ "parameters" : [ { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, "type" : "string", + "default" : "10", "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, "type" : "string", + "default" : "0", "pattern" : "^[0-9]*$" }, { "name" : "wql", @@ -1759,27 +1773,6 @@ "schema" : { "$ref" : "#/definitions/W3CCredentialsListRequest" } - }, { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - }, { - "name" : "wql", - "in" : "query", - "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" } ], "responses" : { "200" : { @@ -4050,9 +4043,10 @@ }, { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, "type" : "string", + "default" : "10", "pattern" : "^[1-9][0-9]*$" }, { "name" : "extra_query", @@ -4061,6 +4055,18 @@ "required" : false, "type" : "string", "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "referent", "in" : "query", @@ -4070,9 +4076,10 @@ }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, "type" : "string", + "default" : "0", "pattern" : "^[0-9]*$" } ], "responses" : { @@ -4389,9 +4396,10 @@ }, { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, "type" : "string", + "default" : "10", "pattern" : "^[1-9][0-9]*$" }, { "name" : "extra_query", @@ -4400,6 +4408,18 @@ "required" : false, "type" : "string", "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "referent", "in" : "query", @@ -4409,9 +4429,10 @@ }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, "type" : "string", + "default" : "0", "pattern" : "^[0-9]*$" } ], "deprecated" : true, diff --git a/poetry.lock b/poetry.lock index d8f5f53957..e306d3256a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,15 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. - -[[package]] -name = "aiofiles" -version = "24.1.0" -description = "File support for asyncio." -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, - {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, -] +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -17,6 +6,7 @@ version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, @@ -24,87 +14,93 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.11" +version = "3.11.12" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -files = [ - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, - {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, - {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, - {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, - {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, - {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, - {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, - {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, - {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, - {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, - {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, +groups = ["main"] +files = [ + {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"}, + {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"}, + {file = "aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae"}, + {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf"}, + {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff"}, + {file = "aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d"}, + {file = "aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9"}, + {file = "aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5"}, + {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804"}, + {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b"}, + {file = "aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16"}, + {file = "aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1"}, + {file = "aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e"}, + {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef"}, + {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9"}, + {file = "aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a"}, + {file = "aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c"}, + {file = "aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e"}, + {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a"}, + {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce"}, + {file = "aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f"}, + {file = "aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78"}, + {file = "aiohttp-3.11.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5"}, + {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4"}, + {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8"}, + {file = "aiohttp-3.11.12-cp39-cp39-win32.whl", hash = "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462"}, + {file = "aiohttp-3.11.12-cp39-cp39-win_amd64.whl", hash = "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798"}, + {file = "aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0"}, ] [package.dependencies] @@ -125,6 +121,7 @@ version = "3.0.3" description = "Build and document REST APIs with aiohttp and apispec" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp_apispec_acapy-3.0.3-py3-none-any.whl", hash = "sha256:9a5d335c22975da1bbde49ddc04c138ee285d7c38354e88b43babef2eec0bc54"}, {file = "aiohttp_apispec_acapy-3.0.3.tar.gz", hash = "sha256:8cec5f2601f8c2d7d53dd4aebab3975a596d86ea3a1a362eb3b1adadc11662b3"}, @@ -142,6 +139,7 @@ version = "0.7.0" description = "CORS support for aiohttp" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "aiohttp-cors-0.7.0.tar.gz", hash = "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d"}, {file = "aiohttp_cors-0.7.0-py3-none-any.whl", hash = "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e"}, @@ -156,6 +154,7 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -170,6 +169,7 @@ version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.10" +groups = ["dev"] files = [ {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, @@ -181,6 +181,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -192,6 +193,7 @@ version = "0.2.0" description = "" optional = false python-versions = ">=3.6.3" +groups = ["main"] files = [ {file = "anoncreds-0.2.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:ec57e224d5f1b8749c3d6ff75bb61229a4f9c31df1ee863835f025c78ec10cd0"}, {file = "anoncreds-0.2.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:55dd0ad8c8611d2f6af158485dbd2f3c9524694ee4eaf1c5558973f1e436f943"}, @@ -205,6 +207,7 @@ version = "6.8.1" description = "A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "apispec-6.8.1-py3-none-any.whl", hash = "sha256:eacba00df745efc9adb2a45cf992300e87938582077e101fb26b78ecf4320beb"}, {file = "apispec-6.8.1.tar.gz", hash = "sha256:f4916cbb7be156963b18f5929a0e42bd2349135834b680a81b12432bcfaa9a39"}, @@ -222,26 +225,25 @@ yaml = ["PyYAML (>=3.10)"] [[package]] name = "aries-askar" -version = "0.3.2" +version = "0.4.3" description = "" optional = false python-versions = ">=3.6.3" +groups = ["main"] files = [ - {file = "aries_askar-0.3.2-py3-none-macosx_10_9_universal2.whl", hash = "sha256:02ddbe1773ce72c57edafff5777a1337d4a678da7484596712949170fb3ca1dc"}, - {file = "aries_askar-0.3.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:176eebcf833bb9974a162fd931c8d67669e4f0145b351ce9cb1289fd2d5a345c"}, - {file = "aries_askar-0.3.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:63f9ab97db4778ced830a6d1135e1f8bd1ca564de27218bd114f1cffbd31b04c"}, - {file = "aries_askar-0.3.2-py3-none-win_amd64.whl", hash = "sha256:6b4253377d5ed167ed94790e49c58584b68f897d2541ac4bb18fd37e9264164b"}, + {file = "aries_askar-0.4.3-py3-none-macosx_10_9_universal2.whl", hash = "sha256:fd4073f4abc7bfe423f3a637cb0d2e5fb1b96cfd0e798ee69e26eb5b9528c3e4"}, + {file = "aries_askar-0.4.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:a787fdbf343efcdb9f26ce75ed934c2d48378cef08ae5ce69e997cba7c3f1992"}, + {file = "aries_askar-0.4.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e21025d841c861c20dbae6201863cb1fe76b395c2eec231835bf8703356bb157"}, + {file = "aries_askar-0.4.3-py3-none-win_amd64.whl", hash = "sha256:ea25cc6bed206a8df9c55b09ebead18edcb669740d1868d884ba88699dc95f67"}, ] -[package.dependencies] -cached-property = ">=1.5.2,<1.6.0" - [[package]] name = "attrs" version = "24.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, @@ -261,6 +263,7 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -275,6 +278,7 @@ version = "2.1.1" description = "Base58 and Base58Check implementation." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, @@ -289,6 +293,7 @@ version = "0.3.0" description = "Python library for general Base-N encodings." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "bases-0.3.0-py3-none-any.whl", hash = "sha256:a2fef3366f3e522ff473d2e95c21523fe8e44251038d5c6150c01481585ebf5b"}, {file = "bases-0.3.0.tar.gz", hash = "sha256:70f04a4a45d63245787f9e89095ca11042685b6b64b542ad916575ba3ccd1570"}, @@ -301,23 +306,13 @@ typing-validation = ">=1.1.0" [package.extras] dev = ["base58", "mypy", "pylint", "pytest", "pytest-cov"] -[[package]] -name = "cached-property" -version = "1.5.2" -description = "A decorator for caching properties in classes." -optional = false -python-versions = "*" -files = [ - {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, - {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, -] - [[package]] name = "cachetools" version = "5.5.1" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, @@ -329,6 +324,7 @@ version = "2.0.0" description = "Canonical JSON" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "canonicaljson-2.0.0-py3-none-any.whl", hash = "sha256:c38a315de3b5a0532f1ec1f9153cd3d716abfc565a558d00a4835428a34fca5b"}, {file = "canonicaljson-2.0.0.tar.gz", hash = "sha256:e2fdaef1d7fadc5d9cb59bd3d0d41b064ddda697809ac4325dced721d12f113f"}, @@ -340,6 +336,7 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, @@ -351,6 +348,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -430,6 +428,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -441,6 +440,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -542,6 +542,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -553,6 +555,7 @@ version = "1.7" description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b"}, {file = "ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1"}, @@ -568,6 +571,7 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -642,6 +646,7 @@ version = "44.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" +groups = ["main"] files = [ {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, @@ -691,6 +696,8 @@ version = "1.0.1" description = "Cython implementation of Toolz: High performance functional utilities" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name == \"cpython\"" files = [ {file = "cytoolz-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cec9af61f71fc3853eb5dca3d42eb07d1f48a4599fa502cbe92adde85f74b042"}, {file = "cytoolz-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:140bbd649dbda01e91add7642149a5987a7c3ccc251f2263de894b89f50b6608"}, @@ -806,6 +813,7 @@ version = "1.8.12" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "debugpy-1.8.12-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:a2ba7ffe58efeae5b8fad1165357edfe01464f9aef25e814e891ec690e7dd82a"}, {file = "debugpy-1.8.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbd4149c4fc5e7d508ece083e78c17442ee13b0e69bfa6bd63003e486770f45"}, @@ -841,6 +849,7 @@ version = "2.0" description = "A toolset for deeply merging Python dictionaries." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00"}, {file = "deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20"}, @@ -855,6 +864,7 @@ version = "0.1.2" description = "An implementation of did:peer:2" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "did_peer_2-0.1.2-py3-none-any.whl", hash = "sha256:d5908cda2d52b7c34428a421044507d7847fd79b78dc8360441c408f4507d612"}, {file = "did_peer_2-0.1.2.tar.gz", hash = "sha256:af8623f62022732e9fadc0289dfb886fd8267767251c4fa0b63694ecd29a7086"}, @@ -869,6 +879,7 @@ version = "0.1.4" description = "An implementation of did:peer:4" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "did_peer_4-0.1.4-py3-none-any.whl", hash = "sha256:4c2bb42a55e4fec08fe008a1585db2f11fe19e36121f8919991add027d7c816f"}, {file = "did_peer_4-0.1.4.tar.gz", hash = "sha256:b367922067b428d33458ca36158eaed40c863cde2fbab6a18a523dccad533c8e"}, @@ -879,29 +890,35 @@ base58 = ">=2.1.1" [[package]] name = "did-webvh" -version = "0.2.1" +version = "0.3.0" description = "This repository includes Python libraries for working with `did:webvh` (did:web + Verified History) DID documents and the underlying log format." optional = false -python-versions = "<4.0,>=3.10" -files = [ - {file = "did_webvh-0.2.1-py3-none-any.whl", hash = "sha256:4c4a84d3c7bda8d82eed1cb181dea1fc0fe4c23d6fe65e3622ff7f61dd7688a3"}, - {file = "did_webvh-0.2.1.tar.gz", hash = "sha256:2e731a594d9b7ff509f70d2595f1435873985b29ce2a489e98ead1a872264892"}, -] +python-versions = ">= 3.10, <4" +groups = ["main"] +files = [] +develop = false [package.dependencies] -aiofiles = ">=24.1.0,<25.0.0" aiohttp = ">=3.10.5,<4.0.0" -aries-askar = ">=0.3.2,<0.4.0" +aries-askar = ">=0.3.2" base58 = ">=2.1.0,<2.2.0" jsoncanon = ">=0.2.3,<0.3.0" multiformats = ">=0.3.1,<0.4.0" +[package.source] +type = "git" +url = "https://github.com/decentralized-identity/didwebvh-py.git" +reference = "HEAD" +resolved_reference = "2ac2b83d5626aaa55307104558a296be41b6528a" + [[package]] name = "didcomm-messaging" version = "0.1.1" description = "DIDComm Messaging implemented with swappable backends." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"didcommv2\"" files = [ {file = "didcomm_messaging-0.1.1-py3-none-any.whl", hash = "sha256:5350006d3a92e085dd208add738d6439c2b0a950c1fe19011ece71de0574cc1a"}, {file = "didcomm_messaging-0.1.1.tar.gz", hash = "sha256:e3c7c7612b8f9710ccd221b2f5e8456cbf868600f7cc10474fcc98be325702eb"}, @@ -923,6 +940,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -934,6 +952,7 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -945,6 +964,7 @@ version = "0.19.0" description = "ECDSA cryptographic signature library (pure python)" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" +groups = ["main"] files = [ {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, @@ -963,6 +983,7 @@ version = "0.7.1" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a"}, {file = "eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5"}, @@ -981,6 +1002,7 @@ version = "5.1.0" description = "eth-typing: Common type annotations for ethereum python packages" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_typing-5.1.0-py3-none-any.whl", hash = "sha256:c0d6b93f5385aa84efc4b47ae2bd478da069bc0ffda8b67e0ccb573f43defd29"}, {file = "eth_typing-5.1.0.tar.gz", hash = "sha256:8581f212ee6252aaa285377a77620f6e5f6e16ac3f144c61f098fafd47967b1a"}, @@ -1000,6 +1022,7 @@ version = "5.2.0" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_utils-5.2.0-py3-none-any.whl", hash = "sha256:4d43eeb6720e89a042ad5b28d4b2111630ae764f444b85cbafb708d7f076da10"}, {file = "eth_utils-5.2.0.tar.gz", hash = "sha256:17e474eb654df6e18f20797b22c6caabb77415a996b3ba0f3cc8df3437463134"}, @@ -1022,6 +1045,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -1036,6 +1060,7 @@ version = "3.17.0" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, @@ -1052,6 +1077,7 @@ version = "2.4.6" description = "A simple immutable dictionary" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "frozendict-2.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a05c0a50cab96b4bb0ea25aa752efbfceed5ccb24c007612bc63e51299336f"}, {file = "frozendict-2.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5b94d5b07c00986f9e37a38dd83c13f5fe3bf3f1ccc8e88edea8fe15d6cd88c"}, @@ -1100,6 +1126,7 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -1201,6 +1228,7 @@ version = "2.6.6" description = "File identification library for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"}, {file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"}, @@ -1215,6 +1243,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1229,6 +1258,7 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -1240,6 +1270,7 @@ version = "1.1.1" description = "" optional = false python-versions = ">=3.6.3" +groups = ["main"] files = [ {file = "indy_credx-1.1.1-py3-none-macosx_10_9_universal2.whl", hash = "sha256:522b90a2362de681e8224b7e5173a9a6093dc48b2ed13599c9eca3df36e29128"}, {file = "indy_credx-1.1.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:05f9a96166f79799c39c62723d78c5480fe9a872dd9dee9fbff1f79d0484c893"}, @@ -1253,6 +1284,7 @@ version = "0.4.2" description = "" optional = false python-versions = ">=3.6.3" +groups = ["main"] files = [ {file = "indy_vdr-0.4.2-py3-none-macosx_10_9_universal2.whl", hash = "sha256:21e4cc22bdb1de581e4abe00e2201d970f46e05d2420437fe023052614867553"}, {file = "indy_vdr-0.4.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9dc8e16e8a0c4666c1a9f0a3e9967cb3dace92975b8dbb9b0aa2c7785ac5e12b"}, @@ -1266,6 +1298,7 @@ version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -1277,6 +1310,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -1288,6 +1322,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -1305,6 +1340,7 @@ version = "0.2.3" description = "Typed Python implementation of JSON Canonicalization Scheme as described in RFC 8785. Currently lacks full floating point support" optional = false python-versions = ">=3.8,<4.0" +groups = ["main"] files = [ {file = "jsoncanon-0.2.3-py3-none-any.whl", hash = "sha256:adb35dac2d0c5dd56f1cb374f1ea6f1fff2ebbb4e844b06d9c96b9ccadf12bf0"}, {file = "jsoncanon-0.2.3.tar.gz", hash = "sha256:483c1ef14e6c8151ba69c0bf646551f249698dd523e9c6da1339a688c5f96d6d"}, @@ -1316,6 +1352,7 @@ version = "1.7.0" description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"}, {file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"}, @@ -1331,6 +1368,7 @@ version = "1.5.6" description = "Implementation of JOSE Web standards" optional = false python-versions = ">= 3.8" +groups = ["main"] files = [ {file = "jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789"}, {file = "jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039"}, @@ -1346,6 +1384,7 @@ version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, @@ -1500,6 +1539,7 @@ version = "3.7" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -1515,6 +1555,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1581,13 +1622,14 @@ files = [ [[package]] name = "marshmallow" -version = "3.26.0" +version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "marshmallow-3.26.0-py3-none-any.whl", hash = "sha256:1287bca04e6a5f4094822ac153c03da5e214a0a60bcd557b140f3e66991b8ca1"}, - {file = "marshmallow-3.26.0.tar.gz", hash = "sha256:eb36762a1cc76d7abf831e18a3a1b26d3d481bbc74581b8e532a3d3a8115e1cb"}, + {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, + {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, ] [package.dependencies] @@ -1604,6 +1646,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -1705,6 +1748,7 @@ version = "0.3.1.post4" description = "Python implementation of multiformats protocols." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "multiformats-0.3.1.post4-py3-none-any.whl", hash = "sha256:5b1d61bd8275c9e817bdbee38dbd501b26629011962ee3c86c46e7ccd0b14129"}, {file = "multiformats-0.3.1.post4.tar.gz", hash = "sha256:d00074fdbc7d603c2084b4c38fa17bbc28173cf2750f51f46fbbc5c4d5605fbb"}, @@ -1726,6 +1770,7 @@ version = "0.3.1" description = "Pre-loading configuration module for the 'multiformats' package." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "multiformats-config-0.3.1.tar.gz", hash = "sha256:7eaa80ef5d9c5ee9b86612d21f93a087c4a655cbcb68960457e61adbc62b47a7"}, {file = "multiformats_config-0.3.1-py3-none-any.whl", hash = "sha256:dec4c9d42ed0d9305889b67440f72e8e8d74b82b80abd7219667764b5b0a8e1d"}, @@ -1743,6 +1788,7 @@ version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, @@ -1754,6 +1800,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1765,6 +1812,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1776,6 +1824,7 @@ version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -1864,6 +1913,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1880,6 +1930,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1895,6 +1946,7 @@ version = "3.11" description = "Python Lex & Yacc" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, @@ -1906,6 +1958,7 @@ version = "3.1.1" description = "Wraps the portalocker recipe for easy usage" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "portalocker-3.1.1-py3-none-any.whl", hash = "sha256:80e984e24de292ff258a5bea0e4f3f778fff84c0ae1275dbaebc4658de4aacb3"}, {file = "portalocker-3.1.1.tar.gz", hash = "sha256:ec20f6dda2ad9ce89fa399a5f31f4f1495f515958f0cb7ca6543cef7bb5a749e"}, @@ -1925,6 +1978,7 @@ version = "4.1.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, @@ -1943,6 +1997,7 @@ version = "2.0.10" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=2.6,<3.0.dev0 || >=3.3.dev0" +groups = ["main"] files = [ {file = "prompt_toolkit-2.0.10-py2-none-any.whl", hash = "sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31"}, {file = "prompt_toolkit-2.0.10-py3-none-any.whl", hash = "sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4"}, @@ -1959,6 +2014,7 @@ version = "0.2.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -2050,6 +2106,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -2061,6 +2118,7 @@ version = "2.10.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, @@ -2081,6 +2139,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -2189,58 +2248,60 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydevd" -version = "3.2.3" +version = "3.3.0" description = "PyDev.Debugger (used in PyDev, PyCharm and VSCode Python)" optional = false python-versions = "*" -files = [ - {file = "pydevd-3.2.3-cp310-cp310-macosx_14_0_universal2.whl", hash = "sha256:49ab46adb65e7816db387a836e6d761b2d091489f43376770942b048bf470568"}, - {file = "pydevd-3.2.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:541093ec04b4e2733003f508959014e20d9825eac68aa3b78337f1e16c0cefc9"}, - {file = "pydevd-3.2.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93cf2ef02c26540a65b6b06d7630f5059278aab06156fb54d17b44a873605bb"}, - {file = "pydevd-3.2.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c048d5dd4956dfb97c37b9720723ab92bd414e5beb8246d6248c8ec1da8465d"}, - {file = "pydevd-3.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7e1ae249a5fe7457c8e46a40156303646e1150c68e70beffa42211abb853ce8a"}, - {file = "pydevd-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:520c0308d65585e8ea66091d0a7a1a1dfdfc22462c370f83f659797c19af5e64"}, - {file = "pydevd-3.2.3-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:7e564123669ea71f6c145f69f3e5dc42bc879125dd4446e3d2ec8414ff00c417"}, - {file = "pydevd-3.2.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ccbf10e2ced035b35cf91457fefb1c9d323a8e81695bec3e0eb769940fd19c7"}, - {file = "pydevd-3.2.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b4859ece94e950d577e293935ce44dbf85df067a4c9f7f3b3f0144479f70a0"}, - {file = "pydevd-3.2.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eede0158b0b0527fa149a733ecbe3a947e3505b603f8935edc4981d3184e07f8"}, - {file = "pydevd-3.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f1faf1dacde1a7057da7347a8ae00ce9db20cf70bb87fa38f1fa951424e10b31"}, - {file = "pydevd-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:824b389e38192143c84cc7f9aedb62712f0f4a4a839c8561d88462c2b1606417"}, - {file = "pydevd-3.2.3-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:07f78aec74b0b579dbfb404c89908c36f5f6f78f759f5e86fb2d7afbdeb5dd2c"}, - {file = "pydevd-3.2.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7836088dc66117607e776c4ec74793f129e823eae83a3f92ec2f129dc4ad0d70"}, - {file = "pydevd-3.2.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bb6115db22af8c4b416b119f230222eabfa2913971de7a1e8a31003e09366a5"}, - {file = "pydevd-3.2.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a3824bf0014ced5c9449564740916073320995347ea78265c2b398e8b5ad96b0"}, - {file = "pydevd-3.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f6edf3145ff45edfd08a24f6784021b38db782be150ea39f74830242845d66d4"}, - {file = "pydevd-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:798f2bb77c28ba7828bcd1d08cc3ce7ba5a038397b9fc96ea66d8edf4f806577"}, - {file = "pydevd-3.2.3-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:bb7493e091ca12cffed93f16723701d12790c29000c9ec9a1f9aa6823ac964e2"}, - {file = "pydevd-3.2.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8950c70e61ff2e3305d4b6fa0442eb7bc9714d6c9abbb365fb0bba872631cc84"}, - {file = "pydevd-3.2.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0fb536f71352814c7ab34a4cf4683c509d7a462f70b3b630b85b82ba9210d6"}, - {file = "pydevd-3.2.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:84ae4c25d600a328e7a2ca2e5e0246bd61137698e39bd285cfcb9fb0c35223d3"}, - {file = "pydevd-3.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96d3cd3eaa601aa74dada5134e7679f678b25b8645cc3c182cbb253cc43d3b90"}, - {file = "pydevd-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:98400c90e39468f49d27426bf85a7549b432ca893ec8f9b67bca9f1952408a05"}, - {file = "pydevd-3.2.3-cp38-cp38-macosx_14_0_universal2.whl", hash = "sha256:fdea5d87436d6788edec901af84623e080861fe89f366d2d989a293ed2c7d569"}, - {file = "pydevd-3.2.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9513697241b6700e149270eba07ca5f7acef57cfd01fe8422596db3516d3441b"}, - {file = "pydevd-3.2.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61db6f7286a5ceba661a557a51d76fa90cd224cd958c8a740ee001bc71cc51b"}, - {file = "pydevd-3.2.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:8527e6691e5abf14a7f789f22dddd150e7bd32eecc79c2b2a2871dfa7469bc9d"}, - {file = "pydevd-3.2.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8bd64d485dbed999ebf1747c7fc0507519eca1ac14aa348b34e4dd81b4f98fa4"}, - {file = "pydevd-3.2.3-cp38-cp38-win_amd64.whl", hash = "sha256:ddbd6d237f515b97ad4a14ea811ffe66f17103054309c0618332c75098880659"}, - {file = "pydevd-3.2.3-cp39-cp39-macosx_14_0_universal2.whl", hash = "sha256:8f0497bd4a40c06117fabb7a0f21c181940a8622ccc4c4a4880bbe329ee285ba"}, - {file = "pydevd-3.2.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa19f2bb245c87cd96b767135f888dc68cf0defb19d769c20190d401a5c05cb5"}, - {file = "pydevd-3.2.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eadb278d173876644751079777efb13606be9d9b7f154c13338883c1be2e7182"}, - {file = "pydevd-3.2.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c00df573cca0e87a19995fc277693dd76cae969db8a2ec9e9077d5fe86837645"}, - {file = "pydevd-3.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d65d02e39fd1fd4a2303d309eba379f49ac16470f83b7a92385d6be0b415e796"}, - {file = "pydevd-3.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:f65fcf9cc4faeac445c9c52948efbe2fafe45a9d800fb28e01f8f05958e5c0e2"}, - {file = "pydevd-3.2.3.tar.gz", hash = "sha256:54adc689a6bfb3508472b3517ee3e763da2774d6c554b8628ff208183d4c96a2"}, +groups = ["dev"] +files = [ + {file = "pydevd-3.3.0-cp310-cp310-macosx_14_0_universal2.whl", hash = "sha256:5a3a6948d09db219754efdd254fb462aa68a76e635cabc9cb7e95669ce161b14"}, + {file = "pydevd-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04fad1696d596e7bc0e938ca6a08bc0abcc9f6e10099b67148c9fe8abdddf36a"}, + {file = "pydevd-3.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aadeec65b783264cc162426e97d3fe967ca2bf742b4cfef362562ca8cd75b829"}, + {file = "pydevd-3.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6221258670994bddbfcf1c878ee40485cdda56dc47f95024c0050248c0023e66"}, + {file = "pydevd-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65044faa27b8ce5f3166ad0bfcd080983aa5244af130f98aa81eab509b3a072d"}, + {file = "pydevd-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fdfef7091745a495341d6fe3c74ff6c6031e1fde0495a49c6887600dddc80ab9"}, + {file = "pydevd-3.3.0-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:46d7438e74c5903ae6ac1cc824cc7df1597f26929ee95379396aa0dd963625d0"}, + {file = "pydevd-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad52f71503825b571fdb00afbec6c9e9989c634b8a8d901e343b56f858b70a49"}, + {file = "pydevd-3.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925dc3f884832d58d241c6f9275cfaf5e8fd1f328a54642d2c601e2c106c1277"}, + {file = "pydevd-3.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:794cd4c20ff8d23c42e44ca69b038e60043d83d6b6cce2ff4e55dd3964679507"}, + {file = "pydevd-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1472dd4ca78c2d5c3b0b165d7f0c9b00b523b3a1d059dbdfe22c75f1a42c34e5"}, + {file = "pydevd-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cb190421435f56b8366a2757281962a8dca31c6ea480cd5e213e24d6418a809c"}, + {file = "pydevd-3.3.0-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:2a47457fd0b45666fbe76b5037c4b4a2c5c9fc87755faf6c8d47accc7d0e4dc6"}, + {file = "pydevd-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c67695b446014c3e893e2443dfc00abf1c1f25983148fa7899c21f32f70428"}, + {file = "pydevd-3.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47737ab44b3365a741ee9e57d43951d7067938169411785cf2d7507cd049869a"}, + {file = "pydevd-3.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a2fa54ee2be7dc19c220e113551c92199c152a4ee348e7c3c105ebc7cff623c"}, + {file = "pydevd-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0490b1d6aa50c0b0b54166ef9605c837411f0134b97e5afa6686f31eba1d830"}, + {file = "pydevd-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c027d2249478d127a146f245d50de9a211296467ec9d21f25febf3ac916623da"}, + {file = "pydevd-3.3.0-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:21506d009947e4766953ee80ae2b7806bb8144d9da2151408a60e727e19dcf24"}, + {file = "pydevd-3.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a779970f39580a84e48aec5ad1cd60ad59c5e7b820d31dae058427fb71a8747"}, + {file = "pydevd-3.3.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206232066a737bdc1ea27e46a61356a8b0cfdbfd3befe667aed2ba267102f72b"}, + {file = "pydevd-3.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8f61b3fbc1b1da52bd16289cf89f7b621c28787e3a6134285d85d79aa43d6fcb"}, + {file = "pydevd-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cc982957d6c75fe42c14a15793ce812a5acc6a4f9baf88785d150a4cdc267b0"}, + {file = "pydevd-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:77d5e8688ddca31a8d342baf3d37b39db3c6c5e639f65f66ae58b9bc6dc47637"}, + {file = "pydevd-3.3.0-cp38-cp38-macosx_14_0_universal2.whl", hash = "sha256:22f0d5f5347e44469ac46cb7bbd8817f7b44754e755e4d770dce13aa26a0aaf4"}, + {file = "pydevd-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acdf76c291ecca0121d5fd8e631246865cf604356c2fb354da9f446ed6cdf17d"}, + {file = "pydevd-3.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce504ee65acca6ed1ccc6e39cc3cf48ada01a318628cc8743fe7dc07e05ebc7a"}, + {file = "pydevd-3.3.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ccbd7a2a74e3b04079fec0eb3e3633cdf15c75eeab0a0dcca3be23c87a13f7f1"}, + {file = "pydevd-3.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:07d0a52e73fb8d6de1449178d0ae6a54acc1234834fb76811eb55e14c73d26f1"}, + {file = "pydevd-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:10d549260fde494664255ef5e245e204fc6c0827f4ba3d486e7f54d202912367"}, + {file = "pydevd-3.3.0-cp39-cp39-macosx_14_0_universal2.whl", hash = "sha256:79c4752d9794b583ee775c1e40d868b567bc79c05b89a58aefc9c8e5c3719976"}, + {file = "pydevd-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7c6b3e34e3b0d8952addc58dcb2aaeb9c8b92a51c7255d3e11356ac7d195594"}, + {file = "pydevd-3.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15527e3b42a90d2b9ca568ef506ee3d0b0f3ebf5770bdc036916b61b2480f253"}, + {file = "pydevd-3.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:150ff02deac2a49a0f3b352766789fdf7313281aafdb78840b11413fbc2ac06e"}, + {file = "pydevd-3.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:512250f1100f361ca7e3a0b0da30b3f2876cb3ca1747deab32c0f5e4c3cd0df4"}, + {file = "pydevd-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:29ae2c91f5d9ebb080d64e7f2cb5d127ccbfbee2edd34cbce90db61ac07647b2"}, + {file = "pydevd-3.3.0.tar.gz", hash = "sha256:aa4bdb74c5e21bde8f396c5055f5e34b0a23a359ec1cc44c6b25282adc9c3f50"}, ] [[package]] name = "pydevd-pycharm" -version = "251.17181.23" +version = "251.18673.39" description = "PyCharm Debugger (used in PyCharm and PyDev)" optional = false python-versions = "*" +groups = ["dev"] files = [ - {file = "pydevd_pycharm-251.17181.23.tar.gz", hash = "sha256:5ba17e4e13e41661d6d4b790bab9e404d268e120d04dd682952d6bf156a6cdfc"}, + {file = "pydevd_pycharm-251.18673.39.tar.gz", hash = "sha256:52c3abb328050d6d367688d0999a8cce1d9bca59cddc0a237f955a14907bd68d"}, ] [[package]] @@ -2249,6 +2310,7 @@ version = "0.5.1" description = "Python library for validating, constructing, and representing DIDs and DID Documents" optional = false python-versions = "<4.0.0,>=3.9.0" +groups = ["main"] files = [ {file = "pydid-0.5.1-py3-none-any.whl", hash = "sha256:be89df79b6267ec1814d49cdd240262c8bbddbfcee9e3aad97a97d521620d6c4"}, {file = "pydid-0.5.1.tar.gz", hash = "sha256:9489a5fbfbecc8dc864c461bb5a0c664895726ab3ca83daf389d91a10146d5e2"}, @@ -2265,6 +2327,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2279,6 +2342,7 @@ version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -2296,6 +2360,7 @@ version = "2.0.4" description = "Python implementation of the JSON-LD API" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "PyLD-2.0.4-py3-none-any.whl", hash = "sha256:6dab9905644616df33f8755489fc9b354ed7d832d387b7d1974b4fbd3b8d2a89"}, {file = "PyLD-2.0.4.tar.gz", hash = "sha256:311e350f0dbc964311c79c28e86f84e195a81d06fef5a6f6ac2a4f6391ceeacc"}, @@ -2318,6 +2383,7 @@ version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -2344,6 +2410,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -2360,13 +2427,14 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.25.2" +version = "0.25.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"}, - {file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"}, + {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, + {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, ] [package.dependencies] @@ -2382,6 +2450,7 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -2400,6 +2469,7 @@ version = "0.4.1" description = "pytest plugin to check ruff requirements." optional = false python-versions = "<4.0,>=3.8" +groups = ["dev"] files = [ {file = "pytest_ruff-0.4.1-py3-none-any.whl", hash = "sha256:69acd5b2ba68d65998c730b5b4d656788193190e45f61a53aa66ef8b390634a4"}, {file = "pytest_ruff-0.4.1.tar.gz", hash = "sha256:2c9a30f15f384c229c881b52ec86cfaf1e79d39530dd7dd5f2d6aebe278f7eb7"}, @@ -2415,6 +2485,7 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -2435,6 +2506,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2449,6 +2521,7 @@ version = "3.2.1" description = "JSON Log Formatter for the Python Logging Package" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python_json_logger-3.2.1-py3-none-any.whl", hash = "sha256:cdc17047eb5374bd311e748b42f99d71223f3b0e186f4206cc5d52aefe85b090"}, {file = "python_json_logger-3.2.1.tar.gz", hash = "sha256:8eb0554ea17cb75b05d2848bc14fb02fbdbd9d6972120781b974380bfa162008"}, @@ -2463,6 +2536,8 @@ version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -2490,6 +2565,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2552,6 +2628,7 @@ version = "8.0" description = "QR Code image generator" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "qrcode-8.0-py3-none-any.whl", hash = "sha256:9fc05f03305ad27a709eb742cf3097fa19e6f6f93bb9e2f039c0979190f6f1b1"}, {file = "qrcode-8.0.tar.gz", hash = "sha256:025ce2b150f7fe4296d116ee9bad455a6643ab4f6e7dce541613a4758cbce347"}, @@ -2572,6 +2649,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2589,49 +2667,51 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rlp" -version = "4.0.1" +version = "4.1.0" description = "rlp: A package for Recursive Length Prefix encoding and decoding" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ - {file = "rlp-4.0.1-py3-none-any.whl", hash = "sha256:ff6846c3c27b97ee0492373aa074a7c3046aadd973320f4fffa7ac45564b0258"}, - {file = "rlp-4.0.1.tar.gz", hash = "sha256:bcefb11013dfadf8902642337923bd0c786dc8a27cb4c21da6e154e52869ecb1"}, + {file = "rlp-4.1.0-py3-none-any.whl", hash = "sha256:8eca394c579bad34ee0b937aecb96a57052ff3716e19c7a578883e767bc5da6f"}, + {file = "rlp-4.1.0.tar.gz", hash = "sha256:be07564270a96f3e225e2c107db263de96b5bc1f27722d2855bd3459a08e95a9"}, ] [package.dependencies] eth-utils = ">=2" [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "hypothesis (==5.19.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +dev = ["build (>=0.9.0)", "bump_my_version (>=0.19.0)", "hypothesis (>=6.22.0,<6.108.7)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)"] rust-backend = ["rusty-rlp (>=0.2.1)"] -test = ["hypothesis (==5.19.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +test = ["hypothesis (>=6.22.0,<6.108.7)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "ruff" -version = "0.9.3" +version = "0.9.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -files = [ - {file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"}, - {file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"}, - {file = "ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4"}, - {file = "ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519"}, - {file = "ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b"}, - {file = "ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c"}, - {file = "ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4"}, - {file = "ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b"}, - {file = "ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a"}, +groups = ["dev"] +files = [ + {file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"}, + {file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"}, + {file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"}, + {file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"}, + {file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"}, + {file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"}, + {file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"}, + {file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"}, + {file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"}, ] [[package]] @@ -2640,6 +2720,7 @@ version = "0.10.4" description = "The reference implementation of the IETF SD-JWT specification." optional = false python-versions = ">=3.8,<4.0" +groups = ["main"] files = [ {file = "sd_jwt-0.10.4-py3-none-any.whl", hash = "sha256:d7ae669eb5d51bceeb38e0df8ab2faddd12e3b21ab64d831b6d048fc1e00ce75"}, {file = "sd_jwt-0.10.4.tar.gz", hash = "sha256:82f93e2f570cfd31fab124e301febb81f3bcad70b10e38f5f9cff70ad659c2ce"}, @@ -2655,6 +2736,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2666,6 +2748,7 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -2677,6 +2760,7 @@ version = "8.1.3" description = "Python documentation generator" optional = false python-versions = ">=3.10" +groups = ["dev"] files = [ {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, @@ -2711,6 +2795,7 @@ version = "3.0.2" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13"}, {file = "sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85"}, @@ -2730,6 +2815,7 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -2746,6 +2832,7 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -2762,6 +2849,7 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -2778,6 +2866,7 @@ version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = false python-versions = ">=2.7" +groups = ["dev"] files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, @@ -2792,6 +2881,7 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -2806,6 +2896,7 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -2822,6 +2913,7 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -2838,6 +2930,8 @@ version = "1.0.0" description = "List processing tools and functional utilities" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name == \"cpython\" or implementation_name == \"pypy\"" files = [ {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"}, {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, @@ -2849,6 +2943,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -2860,6 +2955,7 @@ version = "1.2.11.post4" description = "A simple library for runtime type-checking." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "typing_validation-1.2.11.post4-py3-none-any.whl", hash = "sha256:73dd504ddebf5210e80d5f65ba9b30efbd0fa42f266728fda7c4f0ba335c699c"}, {file = "typing_validation-1.2.11.post4.tar.gz", hash = "sha256:7aed04ecfbda07e63b7266f90e5d096f96344f7facfe04bb081b21e4a9781670"}, @@ -2874,6 +2970,7 @@ version = "0.2.0" description = "Unflatten dict to dict with nested dict/arrays" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "unflatten-0.2.0-py2.py3-none-any.whl", hash = "sha256:a0afa7ff22313dcc60ff45110b796ed5b4e908614826e8672a9f76d3a20c1f54"}, {file = "unflatten-0.2.0.tar.gz", hash = "sha256:9710bc558882f697bc36a95a97614be296f07c8f8df1bc2b4ef96c189ce5cf84"}, @@ -2885,6 +2982,7 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -2902,6 +3000,8 @@ version = "1.0.1" description = "" optional = true python-versions = ">=3.6.3" +groups = ["main"] +markers = "extra == \"bbs\"" files = [ {file = "ursa_bbs_signatures-1.0.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:245701789df0d6cda09faa6194b590c0ce6513b39d80fe8f1fe2a52b69a4b520"}, {file = "ursa_bbs_signatures-1.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:79f410a1f89855a4149af7adecaa22a5f427640f5d093f53670657394a9f9ef4"}, @@ -2914,6 +3014,7 @@ version = "0.10.0" description = "Drop-in replacement for Python UUID in Rust" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d5a4508feefec62456cd6a41bcdde458d56827d908f226803b886d22a3d5e63"}, {file = "uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dbefc2b9113f9dfe56bdae58301a2b3c53792221410d422826f3d1e3e6555fe7"}, @@ -2950,6 +3051,7 @@ version = "20.29.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"}, {file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"}, @@ -2970,6 +3072,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -2981,6 +3084,7 @@ version = "8.6.0" description = "Declarative parsing and validation of HTTP request objects, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, Pyramid, Falcon, and aiohttp." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "webargs-8.6.0-py3-none-any.whl", hash = "sha256:83da4d7105643d0a50499b06d98a6ade1a330ce66d039eaa51f715172c704aba"}, {file = "webargs-8.6.0.tar.gz", hash = "sha256:b8d098ab92bd74c659eca705afa31d681475f218cb15c1e57271fa2103c0547a"}, @@ -3002,6 +3106,7 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -3097,6 +3202,6 @@ bbs = ["ursa-bbs-signatures"] didcommv2 = ["didcomm-messaging"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" -content-hash = "e55ebe3303a5543f08b0794b17de0fca13f65fd1363edc14422f045cf0f52c8d" +content-hash = "4af9f539695c21f944f8a499e732930999210466ecd1345f1c4741de288feaaa" diff --git a/pyproject.toml b/pyproject.toml index 8eb9c6a7e4..d5772778c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ repository = "https://github.com/openwallet-foundation/acapy" [tool.poetry.dependencies] python = "^3.12" -aiohttp = "~3.11.11" +aiohttp = "~3.11.12" aiohttp-apispec-acapy = "~3.0.3" aiohttp-cors = "~0.7.0" apispec = "^6.6.0" @@ -27,7 +27,7 @@ ecdsa = "~0.19.0" jsonpath-ng = "^1.7.0" Markdown = "~3.7" markupsafe = "^3.0.2" -marshmallow = "~3.26.0" +marshmallow = "~3.26.1" nest_asyncio = "~1.6.0" packaging = "^24.2" portalocker = "^3.1.1" @@ -41,7 +41,7 @@ python-json-logger = "^3.2.1" pyyaml = "~6.0.2" qrcode = { version = "^8.0", extras = ["pil"] } requests = "~2.32.3" -rlp = "^4.0.1" +rlp = "^4.1.0" unflatten = "~0.2" sd-jwt = "^0.10.3" uuid_utils = "^0.10.0" @@ -49,14 +49,14 @@ uuid_utils = "^0.10.0" # did libraries did-peer-2 = "^0.1.2" did-peer-4 = "^0.1.4" -did-webvh = "^0.2.1" +did-webvh = ">=0.3.0" # Verifiable Credentials indy-credx = "~1.1.1" anoncreds = "~0.2.0" # askar -aries-askar = "~0.3.2" +aries-askar = ">=0.4.3" # indy indy-vdr = "~0.4.0" @@ -70,20 +70,20 @@ canonicaljson = "^2.0.0" [tool.poetry.group.dev.dependencies] # Sync with version in .pre-commit-config.yaml and .github/workflows/format.yml -ruff = "~0.9.3" +ruff = "~0.9.6" pre-commit = "~4.1.0" sphinx = "^8.1.3" sphinx-rtd-theme = "^3.0.2" -pydevd = "~3.2.3" +pydevd = "~3.3.0" pydevd-pycharm = "^251.17181.23" # testing pytest = "^8.3.4" -pytest-asyncio = "^0.25.2" +pytest-asyncio = "^0.25.3" pytest-cov = "^6.0.0" pytest-ruff = "^0.4.1" pytest-xdist = "^3.6.1" diff --git a/scenarios/examples/anoncreds_issuance_and_revocation/example.py b/scenarios/examples/anoncreds_issuance_and_revocation/example.py index 4489435c4c..d74a813b40 100644 --- a/scenarios/examples/anoncreds_issuance_and_revocation/example.py +++ b/scenarios/examples/anoncreds_issuance_and_revocation/example.py @@ -97,10 +97,14 @@ async def main(): await issuer.post("/wallet/did/public", params=params(did=public_did.did)) - _, cred_def = await indy_anoncred_credential_artifacts( + schema_name = "anoncreds-test-" + token_hex(8) + schema_version = "1.0" + schema, cred_def = await indy_anoncred_credential_artifacts( issuer, ["firstname", "lastname"], support_revocation=True, + schema_name=schema_name, + schema_version=schema_version, ) # Issue a credential @@ -111,6 +115,10 @@ async def main(): holder_anoncreds_conn.connection_id, cred_def.credential_definition_id, {"firstname": "Anoncreds", "lastname": "Holder"}, + issuer_id=public_did.did, + schema_id=schema.schema_id, + schema_issuer_id=public_did.did, + schema_name=schema_name, ) # Present the the credential's attributes @@ -156,6 +164,10 @@ async def main(): holder_indy_conn.connection_id, cred_def.credential_definition_id, {"firstname": "Indy", "lastname": "Holder"}, + issuer_id=public_did.did, + schema_id=schema.schema_id, + schema_issuer_id=public_did.did, + schema_name=schema_name, ) # Present the the credential's attributes @@ -230,12 +242,14 @@ async def main(): # Create a new schema and cred def with different attributes on new # anoncreds endpoints + schema_name = "anoncreds-test-" + token_hex(8) + schema_version = "1.0" schema = await issuer.post( "/anoncreds/schema", json={ "schema": { - "name": "anoncreds-test-" + token_hex(8), - "version": "1.0", + "name": schema_name, + "version": schema_version, "attrNames": ["middlename"], "issuerId": public_did.did, } @@ -263,6 +277,10 @@ async def main(): holder_anoncreds_conn.connection_id, cred_def.credential_definition_state["credential_definition_id"], {"middlename": "Anoncreds"}, + issuer_id=public_did.did, + schema_id=schema.schema_state["schema_id"], + schema_issuer_id=public_did.did, + schema_name=schema_name, ) # Presentation for anoncreds capable holder await anoncreds_present_proof_v2( @@ -294,6 +312,10 @@ async def main(): holder_indy_conn.connection_id, cred_def.credential_definition_state["credential_definition_id"], {"middlename": "Indy"}, + issuer_id=public_did.did, + schema_id=schema.schema_state["schema_id"], + schema_issuer_id=public_did.did, + schema_name=schema_name, ) # Presentation for indy holder await anoncreds_present_proof_v2( diff --git a/scenarios/examples/simple/docker-compose.yml b/scenarios/examples/simple/docker-compose.yml index 7bc9e0852a..199f3d8957 100644 --- a/scenarios/examples/simple/docker-compose.yml +++ b/scenarios/examples/simple/docker-compose.yml @@ -21,12 +21,13 @@ --auto-provision --log-level debug --debug-webhooks + --universal-resolver healthcheck: test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null - start_period: 30s + start_period: 50s interval: 7s timeout: 5s - retries: 5 + retries: 10 depends_on: tails: condition: service_started diff --git a/scenarios/examples/util.py b/scenarios/examples/util.py index d4f128d972..b323b88b86 100644 --- a/scenarios/examples/util.py +++ b/scenarios/examples/util.py @@ -202,6 +202,11 @@ async def anoncreds_issue_credential_v2( holder_connection_id: str, cred_def_id: str, attributes: Mapping[str, str], + issuer_id: Optional[str] = None, + schema_id: Optional[str] = None, + schema_issuer_id: Optional[str] = None, + schema_name: Optional[str] = None, + schema_version: Optional[str] = None, ) -> Tuple[V20CredExRecordDetail, V20CredExRecordDetail]: """Issue an credential using issue-credential/2.0. @@ -217,8 +222,30 @@ async def anoncreds_issue_credential_v2( if is_issuer_anoncreds: _filter = {"anoncreds": {"cred_def_id": cred_def_id}} + if issuer_id: + _filter["anoncreds"]["issuer_id"] = issuer_id + if schema_id: + _filter["anoncreds"]["schema_id"] = schema_id + if schema_issuer_id: + _filter["anoncreds"]["schema_issuer_id"] = schema_issuer_id + if schema_name: + _filter["anoncreds"]["schema_name"] = schema_name + if schema_version: + _filter["anoncreds"]["schema_version"] = schema_version + else: _filter = {"indy": {"cred_def_id": cred_def_id}} + if issuer_id: + _filter["indy"]["issuer_did"] = issuer_id + if schema_id: + _filter["indy"]["schema_id"] = schema_id + if schema_issuer_id: + _filter["indy"]["schema_issuer_did"] = schema_issuer_id + if schema_name: + _filter["indy"]["schema_name"] = schema_name + if schema_version: + _filter["indy"]["schema_version"] = schema_version + issuer_cred_ex = await issuer.post( "/issue-credential-2.0/send-offer", json={ @@ -311,9 +338,7 @@ async def anoncreds_issue_credential_v2( ) return ( - V20CredExRecordDetail( - cred_ex_record=issuer_cred_ex, details=issuer_indy_record - ), + V20CredExRecordDetail(cred_ex_record=issuer_cred_ex, details=issuer_indy_record), V20CredExRecordDetail( cred_ex_record=holder_cred_ex, details=holder_indy_record,