diff --git a/aries_cloudagent/commands/tests/test_provision.py b/aries_cloudagent/commands/tests/test_provision.py index adfa442c40..009bf4ded7 100644 --- a/aries_cloudagent/commands/tests/test_provision.py +++ b/aries_cloudagent/commands/tests/test_provision.py @@ -10,7 +10,7 @@ class TestProvision(AsyncTestCase): def test_bad_calls(self): - with self.assertRaises(ArgsParseError): + with self.assertRaises(SystemExit): test_module.execute([]) with self.assertRaises(SystemExit): diff --git a/aries_cloudagent/commands/tests/test_start.py b/aries_cloudagent/commands/tests/test_start.py index 9f3142cdec..aed77ca53c 100644 --- a/aries_cloudagent/commands/tests/test_start.py +++ b/aries_cloudagent/commands/tests/test_start.py @@ -9,7 +9,7 @@ class TestStart(AsyncTestCase): def test_bad_args(self): - with self.assertRaises(ArgsParseError): + with self.assertRaises(SystemExit): test_module.execute([]) with self.assertRaises(SystemExit): diff --git a/aries_cloudagent/config/argparse.py b/aries_cloudagent/config/argparse.py index 900ec6c6ac..c4ea0bfbea 100644 --- a/aries_cloudagent/config/argparse.py +++ b/aries_cloudagent/config/argparse.py @@ -78,8 +78,10 @@ def get_settings(args: Namespace): for group in group_inst: settings.update(group.get_settings(args)) except ArgsParseError as e: - parser.print_help() - raise e + # this is defined to exit or raise an exception and is + # consistent with an error being raised while the arguments + # are being parsed + parser.error(str(e)) return settings return get_settings @@ -651,6 +653,13 @@ class ProtocolGroup(ArgumentGroup): def add_arguments(self, parser: ArgumentParser): """Add protocol-specific command line arguments to the parser.""" + parser.add_argument( + "--aip-version", + type=BoundedInt(1, 2), + default=1, + env_var="ACAPY_AIP_VERSION", + help="Set the Aries Interop Profile compatibility version (default 1).", + ) parser.add_argument( "--auto-ping-connection", action="store_true", @@ -725,22 +734,6 @@ def add_arguments(self, parser: ArgumentParser): env_var="ACAPY_PRESERVE_EXCHANGE_RECORDS", help="Keep credential exchange records after exchange has completed.", ) - parser.add_argument( - "--emit-new-didcomm-prefix", - action="store_true", - env_var="ACAPY_EMIT_NEW_DIDCOMM_PREFIX", - help="Emit protocol messages with new DIDComm prefix; i.e.,\ - 'https://didcomm.org/' instead of (default) prefix\ - 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/'.", - ) - parser.add_argument( - "--emit-new-didcomm-mime-type", - action="store_true", - env_var="ACAPY_EMIT_NEW_DIDCOMM_MIME_TYPE", - help="Send packed agent messages with the DIDComm MIME type\ - as of RFC 0044; i.e., 'application/didcomm-envelope-enc'\ - instead of 'application/ssi-agent-wire'.", - ) parser.add_argument( "--exch-use-unencrypted-tags", action="store_true", @@ -752,6 +745,7 @@ def add_arguments(self, parser: ArgumentParser): def get_settings(self, args: Namespace) -> dict: """Get protocol settings.""" settings = {} + settings["aip_version"] = args.aip_version if args.auto_ping_connection: settings["auto_ping_connection"] = True if args.invite_base_url: @@ -796,10 +790,6 @@ def get_settings(self, args: Namespace) -> dict: raise ArgsParseError("Error writing trace event " + str(e)) if args.preserve_exchange_records: settings["preserve_exchange_records"] = True - if args.emit_new_didcomm_prefix: - settings["emit_new_didcomm_prefix"] = True - if args.emit_new_didcomm_mime_type: - settings["emit_new_didcomm_mime_type"] = True if args.exch_use_unencrypted_tags: settings["exch_use_unencrypted_tags"] = True environ["EXCH_UNENCRYPTED_TAGS"] = "True" diff --git a/aries_cloudagent/config/wallet.py b/aries_cloudagent/config/wallet.py index ec85ea7b98..6580a480eb 100644 --- a/aries_cloudagent/config/wallet.py +++ b/aries_cloudagent/config/wallet.py @@ -2,6 +2,8 @@ import logging +from typing import Tuple + from ..core.error import ProfileNotFoundError from ..core.profile import Profile, ProfileManager from ..wallet.base import BaseWallet, DIDInfo @@ -17,7 +19,7 @@ async def wallet_config( context: InjectionContext, provision: bool = False -) -> (Profile, DIDInfo): +) -> Tuple[Profile, DIDInfo]: """Initialize the root profile.""" mgr = context.inject(ProfileManager) diff --git a/aries_cloudagent/connections/base_manager.py b/aries_cloudagent/connections/base_manager.py index 3ee4857006..abb3c8e297 100644 --- a/aries_cloudagent/connections/base_manager.py +++ b/aries_cloudagent/connections/base_manager.py @@ -59,6 +59,7 @@ async def create_did_document( inbound_connection_id: str = None, svc_endpoints: Sequence[str] = None, mediation_records: List[MediationRecord] = None, + svc_type: str = None, ) -> DIDDoc: """Create our DID doc for a given DID. @@ -68,6 +69,7 @@ async def create_did_document( svc_endpoints: Custom endpoints for the DID Document mediation_record: The record for mediation that contains routing_keys and service endpoint + svc_type: Customize the type used in service entries Returns: The prepared `DIDDoc` instance @@ -146,7 +148,7 @@ async def create_did_document( service = Service( did_info.did, endpoint_ident, - "IndyAgent", + svc_type or DIDDoc.SERVICE_TYPE_V1, [pk], routing_keys, svc_endpoint, diff --git a/aries_cloudagent/connections/models/diddoc/diddoc.py b/aries_cloudagent/connections/models/diddoc/diddoc.py index d1b8bcb0cc..2bc4ba4eb8 100644 --- a/aries_cloudagent/connections/models/diddoc/diddoc.py +++ b/aries_cloudagent/connections/models/diddoc/diddoc.py @@ -25,11 +25,16 @@ from .publickey import PublicKey, PublicKeyType from .service import Service -from .util import canon_did, canon_ref, ok_did, resource +from .util import canon_ref LOGGER = logging.getLogger(__name__) +def _parseError(msg: str): + LOGGER.debug(msg) + raise ValueError(msg) + + class DIDDoc: """ DID document, grouping a DID with verification keys and services. @@ -38,7 +43,11 @@ class DIDDoc: everything else as URIs (oriented toward W3C-facing operations). """ - CONTEXT = "https://w3id.org/did/v1" + CONTEXT_V0 = "https://w3id.org/did/v1" + CONTEXT_V1 = "https://www.w3.org/ns/did/v1" + + SERVICE_TYPE_V0 = "IndyAgent" + SERVICE_TYPE_V1 = "did-communication" def __init__(self, did: str = None) -> None: """ @@ -55,7 +64,7 @@ def __init__(self, did: str = None) -> None: """ - self._did = canon_did(did) if did else None # allow specification post-hoc + self._did = did self._pubkey = {} self._service = {} @@ -78,7 +87,7 @@ def did(self, value: str) -> None: """ - self._did = canon_did(value) if value else None + self._did = value @property def pubkey(self) -> dict: @@ -121,29 +130,47 @@ def set(self, item: Union[Service, PublicKey]) -> "DIDDoc": "Cannot add item {} to DIDDoc on DID {}".format(item, self.did) ) - def serialize(self) -> dict: + def serialize(self, *, version: int = 0) -> dict: """ Dump current object to a JSON-compatible dictionary. + Args: + version: Define the version of the spec to use in serialization + Returns: dict representation of current DIDDoc """ - return { - "@context": DIDDoc.CONTEXT, - "id": canon_ref(self.did, self.did), - "publicKey": [pubkey.to_dict() for pubkey in self.pubkey.values()], - "authentication": [ - { - "type": pubkey.type.authn_type, - "publicKey": canon_ref(self.did, pubkey.id), - } - for pubkey in self.pubkey.values() - if pubkey.authn - ], - "service": [service.to_dict() for service in self.service.values()], - } + if version == 0: + return { + "@context": DIDDoc.CONTEXT_V0, + "id": self.did, + "publicKey": [pubkey.to_dict() for pubkey in self.pubkey.values()], + "authentication": [ + { + "type": pubkey.type.authn_type, + "publicKey": canon_ref(self.did, pubkey.id), + } + for pubkey in self.pubkey.values() + if pubkey.authn + ], + "service": [service.to_dict() for service in self.service.values()], + } + elif version == 1: + return { + "@context": DIDDoc.CONTEXT_V1, + "id": self.did, + "verificationMethod": [ + pubkey.to_dict() for pubkey in self.pubkey.values() + ], + "authentication": [ + pubkey.id for pubkey in self.pubkey.values() if pubkey.authn + ], + "service": [service.to_dict() for service in self.service.values()], + } + else: + raise ValueError(f"Unsupported version for serialization: {version}") def to_json(self) -> str: """ @@ -228,61 +255,53 @@ def deserialize(cls, did_doc: dict) -> "DIDDoc": """ - rv = None - if "id" in did_doc: - rv = DIDDoc(did_doc["id"]) - else: - # heuristic: get DID to serve as DID document identifier from - # the first OK-looking public key - for section in ("publicKey", "authentication"): - if rv is None and section in did_doc: - for key_spec in did_doc[section]: - try: - pubkey_did = canon_did(resource(key_spec.get("id", ""))) - if ok_did(pubkey_did): - rv = DIDDoc(pubkey_did) - break - except ValueError: # no identifier here, move on to next - break - if rv is None: - LOGGER.debug("no identifier in DID document") - raise ValueError("No identifier in DID document") + if "id" not in did_doc: + _parseError("no identifier in DID document") - for pubkey in did_doc.get( - "publicKey", {} - ): # include all public keys, authentication pubkeys by reference - pubkey_type = PublicKeyType.get(pubkey["type"]) - authn = any( - canon_ref(rv.did, ak.get("publicKey", "")) - == canon_ref(rv.did, pubkey["id"]) - for ak in did_doc.get("authentication", {}) - if isinstance(ak.get("publicKey", None), str) - ) - key = PublicKey( # initialization canonicalizes id - rv.did, - pubkey["id"], - pubkey[pubkey_type.specifier], - pubkey_type, - canon_did(pubkey["controller"]), - authn, - ) - rv.pubkey[key.id] = key + rv = DIDDoc(did_doc["id"]) + auth_key_ids = set() for akey in did_doc.get( "authentication", {} ): # include embedded authentication keys - if "publicKey" not in akey: # not yet got it with public keys + if isinstance(akey, str): + auth_key_ids.add(canon_ref(rv.did, akey)) + elif "publicKey" in akey: + # v0 representation + auth_key_ids.add(canon_ref(rv.did, akey["publicKey"])) + else: pubkey_type = PublicKeyType.get(akey["type"]) key = PublicKey( # initialization canonicalized id rv.did, akey["id"], akey[pubkey_type.specifier], pubkey_type, - canon_did(akey["controller"]), + akey["controller"], True, ) + if key.id in rv.pubkey: + _parseError(f"duplicate key id: {key.id}") rv.pubkey[key.id] = key + pubkeys = did_doc.get("verificationMethod", did_doc.get("publicKey")) or {} + for ( + pubkey + ) in pubkeys: # include all public keys, authentication pubkeys by reference + pubkey_type = PublicKeyType.get(pubkey["type"]) + key = PublicKey( # initialization canonicalizes id + rv.did, + pubkey["id"], + pubkey[pubkey_type.specifier], + pubkey_type, + pubkey["controller"], + False, + ) + if key.id in auth_key_ids: + key.authn = True + if key.id in rv.pubkey: + _parseError(f"duplicate key id: {key.id}") + rv.pubkey[key.id] = key + for service in did_doc.get("service", {}): endpoint = service["serviceEndpoint"] svc = Service( # initialization canonicalizes id diff --git a/aries_cloudagent/connections/models/diddoc/publickey.py b/aries_cloudagent/connections/models/diddoc/publickey.py index 25aa8874fb..9796a3ffca 100644 --- a/aries_cloudagent/connections/models/diddoc/publickey.py +++ b/aries_cloudagent/connections/models/diddoc/publickey.py @@ -21,7 +21,7 @@ from collections import namedtuple from enum import Enum -from .util import canon_did, canon_ref +from .util import canon_ref LinkedDataKeySpec = namedtuple("LinkedDataKeySpec", "ver_type authn_type specifier") @@ -126,11 +126,11 @@ def __init__( """ - self._did = canon_did(did) + self._did = did self._id = canon_ref(self._did, ident) self._value = value self._type = pk_type or PublicKeyType.ED25519_SIG_2018 - self._controller = canon_did(controller) if controller else self._did + self._controller = controller or self._did self._authn = authn @property diff --git a/aries_cloudagent/connections/models/diddoc/service.py b/aries_cloudagent/connections/models/diddoc/service.py index 07dc981758..e0c672c5dc 100644 --- a/aries_cloudagent/connections/models/diddoc/service.py +++ b/aries_cloudagent/connections/models/diddoc/service.py @@ -20,7 +20,7 @@ from typing import List, Sequence, Union -from .util import canon_did, canon_ref +from .util import canon_ref from .publickey import PublicKey @@ -62,7 +62,7 @@ def __init__( """ - self._did = canon_did(did) + self._did = did self._id = canon_ref(self._did, ident, ";") self._type = typ self._recip_keys = ( diff --git a/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py b/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py index be002ddc27..5887ab2b88 100644 --- a/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py +++ b/aries_cloudagent/connections/models/diddoc/tests/test_diddoc.py @@ -16,12 +16,10 @@ """ -import json - -from asynctest import TestCase as AsyncTestCase, mock as async_mock +from asynctest import TestCase as AsyncTestCase from .. import DIDDoc, PublicKey, PublicKeyType, Service -from ..util import canon_did, canon_ref +from ..util import canon_ref class TestDIDDoc(AsyncTestCase): @@ -67,7 +65,7 @@ async def test_basic(self): } dd = DIDDoc.deserialize(dd_in) - assert str(dd) == f"DIDDoc({canon_did(dd_in['id'])})" + assert str(dd) == f"DIDDoc({dd_in['id']})" assert len(dd.pubkey) == len(dd_in["publicKey"]) assert len(dd.authnkey) == len(dd_in["authentication"]) @@ -97,7 +95,6 @@ async def test_basic(self): # Exercise accessors dd.did = dd_out["id"] - assert dd.did == canon_did(dd_out["id"]) with self.assertRaises(ValueError): dd.set(["neither a service", "nor a public key"]) assert dd.service[[k for k in dd.service][0]].did == dd.did @@ -198,50 +195,21 @@ def test_reference_authkey(self): # print('\n\n== 5 == DID Doc on canonical refs: {}'.format(ppjson(dd_out))) def test_minimal(self): - # Minimal as per indy-agent test suite without explicit identifiers - dd_in = { - "@context": "https://w3id.org/did/v1", - "publicKey": [ - { - "id": "LjgpST2rjsoxYegQDRm7EL#keys-1", - "type": "Ed25519VerificationKey2018", - "controller": "LjgpST2rjsoxYegQDRm7EL", - "publicKeyBase58": "~XXXXXXXXXXXXXXXX", - } - ], - "service": [ - { - "type": "DidMessaging", - "recipientKeys": ["~XXXXXXXXXXXXXXXX"], - "serviceEndpoint": "https://www.von.ca", - } - ], - } - - dd = DIDDoc.deserialize(dd_in) - assert len(dd.pubkey) == len(dd_in["publicKey"]) - assert len(dd.authnkey) == 0 - - dd_out = dd.serialize() - # print('\n\n== 6 == DID Doc miminal style, implcit DID document identifier: {}'.format( - # ppjson(dd_out))) - - def test_minimal_ids(self): # Minimal + ids as per indy-agent test suite with explicit identifiers; novel service recipient key on raw base58 dd_in = { "@context": "https://w3id.org/did/v1", - "id": "LjgpST2rjsoxYegQDRm7EL", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKey": [ { - "id": "LjgpST2rjsoxYegQDRm7EL#keys-1", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-1", "type": "Ed25519VerificationKey2018", - "controller": "LjgpST2rjsoxYegQDRm7EL", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyBase58": "~XXXXXXXXXXXXXXXX", } ], "service": [ { - "id": "LjgpST2rjsoxYegQDRm7EL;indy", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL;indy", "type": "DidMessaging", "priority": 1, "recipientKeys": ["~YYYYYYYYYYYYYYYY"], @@ -262,24 +230,24 @@ def test_minimal_explicit(self): # Minimal + ids as per indy-agent test suite with explicit identifiers; novel service recipient key on raw base58 dd_in = { "@context": "https://w3id.org/did/v1", - "id": "LjgpST2rjsoxYegQDRm7EL", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKey": [ { - "id": "LjgpST2rjsoxYegQDRm7EL#keys-1", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-1", "type": "Ed25519VerificationKey2018", - "controller": "LjgpST2rjsoxYegQDRm7EL", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyBase58": "~XXXXXXXXXXXXXXXX", }, { - "id": "LjgpST2rjsoxYegQDRm7EL#keys-2", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-2", "type": "Ed25519VerificationKey2018", - "controller": "LjgpST2rjsoxYegQDRm7EL", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyBase58": "~YYYYYYYYYYYYYYYY", }, { - "id": "LjgpST2rjsoxYegQDRm7EL#keys-3", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-3", "type": "Secp256k1VerificationKey2018", - "controller": "LjgpST2rjsoxYegQDRm7EL", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71", }, { @@ -291,7 +259,7 @@ def test_minimal_explicit(self): ], "service": [ { - "id": "LjgpST2rjsoxYegQDRm7EL;indy", + "id": "did:sov:LjgpST2rjsoxYegQDRm7EL;indy", "type": "DidMessaging", "priority": 0, "recipientKeys": ["~ZZZZZZZZZZZZZZZZ"], @@ -306,7 +274,7 @@ def test_minimal_explicit(self): "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-1", ], "routingKeys": ["did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4"], - "serviceEndpoint": "LjgpST2rjsoxYegQDRm7EL;2", + "serviceEndpoint": "did:sov:LjgpST2rjsoxYegQDRm7EL;2", }, { "id": "2", @@ -342,7 +310,7 @@ def test_minimal_explicit(self): ) ) - dd_out = dd.serialize() + _ = dd.serialize() # print('\n\n== 8 == DID Doc on mixed service routing and recipient keys: {}'.format(ppjson(dd_out))) pk = PublicKey( @@ -448,22 +416,6 @@ def test_no_ident(self): dd = DIDDoc.deserialize(dd_in) # print('\n\n== 12 == DID Doc without identifier rejected as expected') - def test_canon_did(self): - # Exercise reference canonicalization, including failure paths - valid_did = "LjgpST2rjsoxYegQDRm7EL" - - with self.assertRaises(ValueError): - canon_ref("not-a-DID", ref=valid_did, delimiter="#") - - with self.assertRaises(ValueError): - canon_ref(valid_did, ref="did:sov:not-a-DID", delimiter="#") - - urlref = ( - "https://www.clafouti-quasar.ca:8443/supply-management/fruit/index.html" - ) - assert canon_ref(valid_did, ref=urlref) == urlref - # print('\n\n== 13 == Reference canonicalization operates as expected') - def test_pubkey_type(self): dd_in = { "@context": "https://w3id.org/did/v1", diff --git a/aries_cloudagent/connections/models/diddoc/util.py b/aries_cloudagent/connections/models/diddoc/util.py index 7d838eff64..ca9f4425d1 100644 --- a/aries_cloudagent/connections/models/diddoc/util.py +++ b/aries_cloudagent/connections/models/diddoc/util.py @@ -38,30 +38,6 @@ def resource(ref: str, delimiter: str = None) -> str: return ref.split(delimiter if delimiter else "#")[0] -def canon_did(uri: str) -> str: - """ - Convert a URI into a DID if need be, left-stripping 'did:sov:' if present. - - Args: - uri: input URI or DID - - Raises: - ValueError: for invalid input. - - """ - - if ok_did(uri): - return uri - - if uri.startswith("did:sov:"): - rv = uri[8:] - if ok_did(rv): - return rv - raise ValueError( - "Bad specification {} does not correspond to a sovrin DID".format(uri) - ) - - def canon_ref(did: str, ref: str, delimiter: str = None): """ Given a reference in a DID document, return it in its canonical form of a URI. @@ -74,27 +50,13 @@ def canon_ref(did: str, ref: str, delimiter: str = None): introducing identifier (';') against DID resource """ - if not ok_did(did): - raise ValueError("Bad DID {} cannot act as DID document identifier".format(did)) - - if ok_did(ref): # e.g., LjgpST2rjsoxYegQDRm7EL - return "did:sov:{}".format(did) - - if ok_did(resource(ref, delimiter)): # e.g., LjgpST2rjsoxYegQDRm7EL#keys-1 - return "did:sov:{}".format(ref) - - if ref.startswith( - "did:sov:" - ): # e.g., did:sov:LjgpST2rjsoxYegQDRm7EL, did:sov:LjgpST2rjsoxYegQDRm7EL#3 - rv = ref[8:] - if ok_did(resource(rv, delimiter)): - return ref - raise ValueError("Bad URI {} does not correspond to a sovrin DID".format(ref)) - - if urlparse(ref).scheme: # e.g., https://example.com/messages/8377464 + if ( + ref.startswith("did:") + or urlparse(ref).scheme # e.g., https://example.com/messages/8377464 + ): return ref - return "did:sov:{}{}{}".format(did, delimiter if delimiter else "#", ref) # e.g., 3 + return "{}{}{}".format(did, delimiter if delimiter else "#", ref) def ok_did(token: str) -> bool: diff --git a/aries_cloudagent/messaging/valid.py b/aries_cloudagent/messaging/valid.py index b97e71fa23..d922f6c6cf 100644 --- a/aries_cloudagent/messaging/valid.py +++ b/aries_cloudagent/messaging/valid.py @@ -194,7 +194,7 @@ class IndyDID(Regexp): """Validate value against indy DID.""" EXAMPLE = "WgWxqztrNooG92RXvxSTWv" - PATTERN = rf"^(did:sov:)?[{B58}]{{21,22}}$" + PATTERN = rf"^(did:sov:|did:peer:)?[{B58}]{{21,22}}$" def __init__(self): """Initializer.""" diff --git a/aries_cloudagent/protocols/connections/v1_0/manager.py b/aries_cloudagent/protocols/connections/v1_0/manager.py index 8b437dd8a4..f247e67215 100644 --- a/aries_cloudagent/protocols/connections/v1_0/manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/manager.py @@ -1,5 +1,6 @@ """Classes to manage connections.""" +from aries_cloudagent.connections.models.diddoc.diddoc import DIDDoc import logging from typing import Coroutine, Sequence, Tuple @@ -34,6 +35,8 @@ from .messages.problem_report import ProblemReportReason from .models.connection_detail import ConnectionDetail +LOGGER = logging.getLogger(__name__) + class ConnectionManagerError(BaseError): """Connection error.""" @@ -50,7 +53,6 @@ def __init__(self, session: ProfileSession): session: The profile session for this connection manager """ self._session = session - self._logger = logging.getLogger(__name__) super().__init__(self._session) @property @@ -332,7 +334,7 @@ async def receive_invitation( self._session, connection.connection_id ) else: - self._logger.debug("Connection invitation will await acceptance") + LOGGER.debug("Connection invitation will await acceptance") return connection async def create_request( @@ -378,7 +380,7 @@ async def create_request( my_info = await wallet.get_local_did(connection.my_did) else: # Create new DID for connection - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") connection.my_did = my_info.did mediation_mgr = MediationManager(self._session.profile) keylist_updates = await mediation_mgr.add_key( @@ -406,6 +408,7 @@ async def create_request( mediation_records=list( filter(None, [base_mediation_record, mediation_record]) ), + svc_type=DIDDoc.SERVICE_TYPE_V0, ) if not my_label: @@ -497,7 +500,7 @@ async def receive_request( if connection.is_multiuse_invitation: wallet = self._session.inject(BaseWallet) - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates ) @@ -554,7 +557,7 @@ async def receive_request( elif not self._session.settings.get("public_invites"): raise ConnectionManagerError("Public invitations are not enabled") else: # request from public did - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") # send update-keylist message with new recipient keys keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates @@ -605,7 +608,7 @@ async def receive_request( self._session, connection.connection_id ) else: - self._logger.debug("Connection request will await acceptance") + LOGGER.debug("Connection request will await acceptance") return connection @@ -657,7 +660,7 @@ async def create_response( if connection.my_did: my_info = await wallet.get_local_did(connection.my_did) else: - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") connection.my_did = my_info.did mediation_mgr = MediationManager(self._session.profile) keylist_updates = await mediation_mgr.add_key( @@ -684,6 +687,7 @@ async def create_response( mediation_records=list( filter(None, [base_mediation_record, mediation_record]) ), + svc_type=DIDDoc.SERVICE_TYPE_V0, ) response = ConnectionResponse( @@ -873,7 +877,7 @@ async def create_static_connection( base_mediation_record = None # seed and DID optional - my_info = await wallet.create_local_did(my_seed, my_did) + my_info = await wallet.create_local_did(seed=my_seed, did=my_did) # must provide their DID and verkey if the seed is not known if (not their_did or not their_verkey) and not their_seed: @@ -911,6 +915,7 @@ async def create_static_connection( mediation_records=[base_mediation_record] if base_mediation_record else None, + svc_type=DIDDoc.SERVICE_TYPE_V0, ) await self.store_did_document(did_doc) @@ -1033,7 +1038,7 @@ async def resolve_inbound_connection(self, receipt: MessageReceipt) -> ConnRecor try: receipt.sender_did = await self.find_did_for_key(receipt.sender_verkey) except StorageNotFoundError: - self._logger.warning( + LOGGER.warning( "No corresponding DID found for sender verkey: %s", receipt.sender_verkey, ) @@ -1048,13 +1053,13 @@ async def resolve_inbound_connection(self, receipt: MessageReceipt) -> ConnRecor if "public" in my_info.metadata and my_info.metadata["public"] is True: receipt.recipient_did_public = True except InjectionError: - self._logger.warning( + LOGGER.warning( "Cannot resolve recipient verkey, no wallet defined by " "context: %s", receipt.recipient_verkey, ) except WalletNotFoundError: - self._logger.warning( + LOGGER.warning( "No corresponding DID found for recipient verkey: %s", receipt.recipient_verkey, ) @@ -1112,7 +1117,7 @@ async def establish_inbound( my_info = await wallet.get_local_did(connection.my_did) else: # Create new DID for connection - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") connection.my_did = my_info.did try: diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py index b45626b9d0..4057d4164f 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py @@ -57,7 +57,7 @@ def make_did_doc(self, did, verkey): async def setUp(self): self.test_seed = "testseed000000000000000000000001" - self.test_did = "55GkHamhTU1ZbTbV2ab9DE" + self.test_did = "did:peer:55GkHamhTU1ZbTbV2ab9DE" self.test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" self.test_endpoint = "http://localhost" @@ -520,13 +520,14 @@ async def test_create_request_mediation_id(self): mediation_id=mediation_record.mediation_id, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with() + create_local_did.assert_called_once_with(method_name="peer") create_did_document.assert_called_once_with( self.manager, did_info, None, [self.test_endpoint], mediation_records=[mediation_record], + svc_type=DIDDoc.SERVICE_TYPE_V0, ) mock_get_default_mediator.assert_not_called() @@ -574,13 +575,14 @@ async def test_create_request_default_mediator(self): record, my_endpoint=self.test_endpoint, ) - create_local_did.assert_called_once_with() + create_local_did.assert_called_once_with(method_name="peer") create_did_document.assert_called_once_with( self.manager, did_info, None, [self.test_endpoint], mediation_records=[mediation_record], + svc_type=DIDDoc.SERVICE_TYPE_V0, ) mock_get_default_mediator.assert_called_once() @@ -1399,9 +1401,14 @@ async def test_create_static_connection_multitenant_mediator(self): None, [self.test_endpoint], mediation_records=[default_mediator], + svc_type=DIDDoc.SERVICE_TYPE_V0, ), call( - their_info, None, [self.test_endpoint], mediation_records=None + their_info, + None, + [self.test_endpoint], + mediation_records=None, + svc_type=DIDDoc.SERVICE_TYPE_V0, ), ] ) diff --git a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py index fdd7e88b24..b878ffbea9 100644 --- a/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py +++ b/aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py @@ -99,7 +99,9 @@ async def _create_routing_did(self, session: ProfileSession) -> DIDInfo: """ wallet = session.inject(BaseWallet) storage = session.inject(BaseStorage) - info = await wallet.create_local_did(metadata={"type": "routing_did"}) + info = await wallet.create_local_did( + method_name="peer", metadata={"type": "routing_did"} + ) record = StorageRecord( type=self.ROUTING_DID_RECORD_TYPE, value=json.dumps({"verkey": info.verkey, "metadata": info.metadata}), diff --git a/aries_cloudagent/protocols/didcomm_prefix.py b/aries_cloudagent/protocols/didcomm_prefix.py index ccb524acd7..2d6ccc6db3 100644 --- a/aries_cloudagent/protocols/didcomm_prefix.py +++ b/aries_cloudagent/protocols/didcomm_prefix.py @@ -26,7 +26,7 @@ def set(settings: Mapping): environ["DIDCOMM_PREFIX"] = ( DIDCommPrefix.NEW.value - if settings.get("emit_new_didcomm_prefix") + if settings.get("aip_version", 1) >= 2 else DIDCommPrefix.OLD.value ) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 077508f3df..fd6cc9f6b8 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -28,6 +28,8 @@ from .messages.response import DIDXResponse from .messages.problem_report import ProblemReportReason +LOGGER = logging.getLogger(__name__) + class DIDXManagerError(BaseError): """Connection error.""" @@ -44,7 +46,6 @@ def __init__(self, session: ProfileSession): session: The profile session for this did exchange manager """ self._session = session - self._logger = logging.getLogger(__name__) super().__init__(self._session) @property @@ -148,7 +149,7 @@ async def receive_invitation( conn_rec.state = ConnRecord.State.REQUEST.rfc23 await conn_rec.save(self._session, reason="Sent connection request") else: - self._logger.debug("Connection invitation will await acceptance") + LOGGER.debug("Connection invitation will await acceptance") return conn_rec @@ -234,7 +235,7 @@ async def create_request( my_info = await wallet.get_local_did(conn_rec.my_did) else: # Create new DID for connection - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") conn_rec.my_did = my_info.did keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates @@ -261,7 +262,8 @@ async def create_request( ), ) pthid = conn_rec.invitation_msg_id or f"did:sov:{conn_rec.their_public_did}" - attach = AttachDecorator.data_base64(did_doc.serialize()) + LOGGER.error(did_doc.serialize(version=1)) + attach = AttachDecorator.data_base64(did_doc.serialize(version=1)) await attach.data.sign(my_info.verkey, wallet) if not my_label: my_label = self._session.settings.get("default_label") @@ -362,7 +364,7 @@ async def receive_request( connection_key = conn_rec.invitation_key if conn_rec.is_multiuse_invitation: wallet = self._session.inject(BaseWallet) - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates ) @@ -432,7 +434,7 @@ async def receive_request( ) else: # request is against implicit invitation on public DID - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates @@ -493,7 +495,7 @@ async def receive_request( conn_rec.state = ConnRecord.State.RESPONSE.rfc23 await conn_rec.save(self._session, reason="Sent connection response") else: - self._logger.debug("DID exchange request will await acceptance") + LOGGER.debug("DID exchange request will await acceptance") return conn_rec @@ -543,7 +545,7 @@ async def create_response( if conn_rec.my_did: my_info = await wallet.get_local_did(conn_rec.my_did) else: - my_info = await wallet.create_local_did() + my_info = await wallet.create_local_did(method_name="peer") conn_rec.my_did = my_info.did keylist_updates = await mediation_mgr.add_key( my_info.verkey, keylist_updates @@ -569,7 +571,7 @@ async def create_response( filter(None, [base_mediation_record, mediation_record]) ), ) - attach = AttachDecorator.data_base64(did_doc.serialize()) + attach = AttachDecorator.data_base64(did_doc.serialize(version=1)) await attach.data.sign(conn_rec.invitation_key, wallet) response = DIDXResponse(did=my_info.did, did_doc_attach=attach) # Assign thread information diff --git a/aries_cloudagent/protocols/tests/test_didcomm_prefix.py b/aries_cloudagent/protocols/tests/test_didcomm_prefix.py index be0dd6b6dd..d0b67789b0 100644 --- a/aries_cloudagent/protocols/tests/test_didcomm_prefix.py +++ b/aries_cloudagent/protocols/tests/test_didcomm_prefix.py @@ -10,13 +10,13 @@ def test_didcomm_prefix(self): DIDCommPrefix.set({}) assert environ.get("DIDCOMM_PREFIX") == DIDCommPrefix.OLD.value - DIDCommPrefix.set({"emit_new_didcomm_prefix": True}) + DIDCommPrefix.set({"aip_version": 2}) assert environ.get("DIDCOMM_PREFIX") == DIDCommPrefix.NEW.value assert DIDCommPrefix.qualify_current("hello") == ( f"{DIDCommPrefix.NEW.value}/hello" ) - DIDCommPrefix.set({"emit_new_didcomm_prefix": False}) + DIDCommPrefix.set({"aip_version": 1}) assert environ.get("DIDCOMM_PREFIX") == DIDCommPrefix.OLD.value assert DIDCommPrefix.qualify_current("hello") == ( f"{DIDCommPrefix.OLD.value}/hello" diff --git a/aries_cloudagent/transport/inbound/http.py b/aries_cloudagent/transport/inbound/http.py index ea81f7089b..21970b3fce 100644 --- a/aries_cloudagent/transport/inbound/http.py +++ b/aries_cloudagent/transport/inbound/http.py @@ -111,9 +111,7 @@ async def inbound_message_handler(self, request: web.BaseRequest): status=200, headers={ "Content-Type": DIDCOMM_V1_MIME_TYPE - if session.profile.settings.get( - "emit_new_didcomm_mime_type" - ) + if session.profile.settings.get("aip_version", 1) >= 2 else DIDCOMM_V0_MIME_TYPE }, ) diff --git a/aries_cloudagent/transport/outbound/http.py b/aries_cloudagent/transport/outbound/http.py index 5bd2069c28..46d9d1911d 100644 --- a/aries_cloudagent/transport/outbound/http.py +++ b/aries_cloudagent/transport/outbound/http.py @@ -68,7 +68,7 @@ async def handle_message( if api_key is not None: headers["x-api-key"] = api_key if isinstance(payload, bytes): - if profile.settings.get("emit_new_didcomm_mime_type"): + if profile.settings.get("aip_version", 1) >= 2: headers["Content-Type"] = DIDCOMM_V1_MIME_TYPE else: headers["Content-Type"] = DIDCOMM_V0_MIME_TYPE diff --git a/aries_cloudagent/transport/outbound/tests/test_http_transport.py b/aries_cloudagent/transport/outbound/tests/test_http_transport.py index a8cc6bf344..bafdc6e738 100644 --- a/aries_cloudagent/transport/outbound/tests/test_http_transport.py +++ b/aries_cloudagent/transport/outbound/tests/test_http_transport.py @@ -94,7 +94,7 @@ async def send_message(transport, payload, endpoint): transport = HttpTransport() - self.profile.settings["emit_new_didcomm_mime_type"] = True + self.profile.settings["aip_version"] = 2 await asyncio.wait_for( send_message(transport, b"{}", endpoint=server_addr), 5.0 ) diff --git a/aries_cloudagent/wallet/base.py b/aries_cloudagent/wallet/base.py index 4b08053b7e..d70a2629c3 100644 --- a/aries_cloudagent/wallet/base.py +++ b/aries_cloudagent/wallet/base.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from collections import namedtuple -from typing import Sequence +from typing import Sequence, Tuple from ..ledger.base import BaseLedger from ..ledger.endpoint_type import EndpointType @@ -88,7 +88,11 @@ async def rotate_did_keypair_apply(self, did: str) -> None: @abstractmethod async def create_local_did( - self, seed: str = None, did: str = None, metadata: dict = None + self, + seed: str = None, + did: str = None, + method_name: str = None, + metadata: dict = None, ) -> DIDInfo: """ Create and store a new local DID. @@ -96,6 +100,7 @@ async def create_local_did( Args: seed: Optional seed to use for DID did: The DID to use + method_name: The DID method to use metadata: Metadata to store with DID Returns: @@ -104,7 +109,11 @@ async def create_local_did( """ async def create_public_did( - self, seed: str = None, did: str = None, metadata: dict = {} + self, + seed: str = None, + did: str = None, + method_name: str = None, + metadata: dict = {}, ) -> DIDInfo: """ Create and store a new public DID. @@ -114,6 +123,7 @@ async def create_public_did( Args: seed: Optional seed to use for DID did: The DID to use + method_name: The DID method to use metadata: Metadata to store with DID Returns: @@ -127,7 +137,7 @@ async def create_public_did( info_meta = info.metadata info_meta["public"] = False await self.replace_local_did_metadata(info.did, info_meta) - return await self.create_local_did(seed, did, metadata) + return await self.create_local_did(seed, did, method_name, metadata) async def get_public_did(self) -> DIDInfo: """ @@ -312,7 +322,7 @@ async def pack_message( """ @abstractmethod - async def unpack_message(self, enc_message: bytes) -> (str, str, str): + async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: """ Unpack a message. diff --git a/aries_cloudagent/wallet/in_memory.py b/aries_cloudagent/wallet/in_memory.py index fce6c46aef..c7f6589f2d 100644 --- a/aries_cloudagent/wallet/in_memory.py +++ b/aries_cloudagent/wallet/in_memory.py @@ -1,7 +1,7 @@ """In-memory implementation of BaseWallet interface.""" import asyncio -from typing import Sequence +from typing import Sequence, Tuple from ..core.in_memory import InMemoryProfile @@ -152,7 +152,11 @@ async def rotate_did_keypair_apply(self, did: str) -> None: return DIDInfo(did, verkey_enc, self.profile.local_dids[did]["metadata"].copy()) async def create_local_did( - self, seed: str = None, did: str = None, metadata: dict = None + self, + seed: str = None, + did: str = None, + method_name: str = None, + metadata: dict = None, ) -> DIDInfo: """ Create and store a new local DID. @@ -160,6 +164,7 @@ async def create_local_did( Args: seed: Optional seed to use for DID did: The DID to use + method_name: The DID method to use metadata: Metadata to store with DID Returns: @@ -174,6 +179,8 @@ async def create_local_did( verkey_enc = bytes_to_b58(verkey) if not did: did = bytes_to_b58(verkey[:16]) + if method_name: + did = f"did:{method_name}{did}" if ( did in self.profile.local_dids and self.profile.local_dids[did]["verkey"] != verkey_enc @@ -371,7 +378,7 @@ async def pack_message( ) return result - async def unpack_message(self, enc_message: bytes) -> (str, str, str): + async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: """ Unpack a message. diff --git a/aries_cloudagent/wallet/indy.py b/aries_cloudagent/wallet/indy.py index b596638a62..f8ffdadcdc 100644 --- a/aries_cloudagent/wallet/indy.py +++ b/aries_cloudagent/wallet/indy.py @@ -2,7 +2,7 @@ import json -from typing import Sequence +from typing import Sequence, Tuple import indy.anoncreds import indy.did @@ -163,7 +163,11 @@ async def rotate_did_keypair_apply(self, did: str) -> DIDInfo: ) from x_indy async def create_local_did( - self, seed: str = None, did: str = None, metadata: dict = None + self, + seed: str = None, + did: str = None, + method_name: str = None, + metadata: dict = None, ) -> DIDInfo: """ Create and store a new local DID. @@ -171,6 +175,7 @@ async def create_local_did( Args: seed: Optional seed to use for DID did: The DID to use + method_name: The DID method to use metadata: Metadata to store with DID Returns: @@ -186,6 +191,9 @@ async def create_local_did( cfg["seed"] = bytes_to_b64(validate_seed(seed)) if did: cfg["did"] = did + if method_name: + # FIXME - use an application default method if not specified? + cfg["method_name"] = method_name did_json = json.dumps(cfg) # crypto_type, cid - optional parameters skipped try: @@ -434,7 +442,7 @@ async def pack_message( return result - async def unpack_message(self, enc_message: bytes) -> (str, str, str): + async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]: """ Unpack a message. diff --git a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py index 03a3b73437..855481eea9 100644 --- a/aries_cloudagent/wallet/tests/test_in_memory_wallet.py +++ b/aries_cloudagent/wallet/tests/test_in_memory_wallet.py @@ -142,7 +142,7 @@ async def test_local_verkey(self, wallet): @pytest.mark.asyncio async def test_local_metadata(self, wallet): info = await wallet.create_local_did( - self.test_seed, self.test_did, self.test_metadata + self.test_seed, self.test_did, None, self.test_metadata ) assert info.did == self.test_did assert info.verkey == self.test_verkey @@ -167,7 +167,7 @@ async def test_local_metadata(self, wallet): @pytest.mark.asyncio async def test_create_public_did(self, wallet): info = await wallet.create_local_did( - self.test_seed, self.test_did, self.test_metadata + self.test_seed, self.test_did, None, self.test_metadata ) assert not info.metadata.get("public") assert not info.metadata.get("posted") @@ -193,7 +193,7 @@ async def test_create_public_did(self, wallet): @pytest.mark.asyncio async def test_set_public_did(self, wallet): info = await wallet.create_local_did( - self.test_seed, self.test_did, self.test_metadata + self.test_seed, self.test_did, None, self.test_metadata ) assert not info.metadata.get("public") diff --git a/aries_cloudagent/wallet/tests/test_indy_wallet.py b/aries_cloudagent/wallet/tests/test_indy_wallet.py index 6360ce85c8..831f36864c 100644 --- a/aries_cloudagent/wallet/tests/test_indy_wallet.py +++ b/aries_cloudagent/wallet/tests/test_indy_wallet.py @@ -165,7 +165,7 @@ async def test_get_local_did_x(self, wallet): @pytest.mark.asyncio async def test_replace_local_did_metadata_x(self, wallet): info = await wallet.create_local_did( - self.test_seed, self.test_did, self.test_metadata + self.test_seed, self.test_did, None, self.test_metadata ) assert info.did == self.test_did assert info.verkey == self.test_verkey